Introduction
The repository pattern separates the retrieves data model mapping logic from the business logic. We will create a repository class for each entity class in our application. All these classes usually have a main group of equals methods such as:
All
GetData
Find
Add
Remove
Update
- Etc.
All these classes have the very similar code and have very similar tests.
Create a generic repository can save time and code.
These are its main advantages:
- Reduction code
- Reduction tests (You test only the repository tests or new code in derived repository class)
- Grow the tests coverage
- Reduces development time
- Improved maintenance
This article will to try to explain how to build a generic repository, step by step and from scratch.
Index
- Generic Repositories Types
Set<TEntity> DbContext
method - Example Classes
- Entity Framework Generic Repositories Disconnected
- Building Entity Framework Generic Repositories Disconnected
All
/ AllAsync
Find
/ FindAsync
GetData
/ GetDataAsync
Add
/ AddAsync
Remove
/ RemoveAsync
Update
/ UpdateAsync
- Extracting the Interface
- MVC Example
- WPF Example
- Extending
DisconGeneriRepository<TEntitiy>
- Test Project
Generic Repositories Types
This generic repositories type is focused on Entity Framework technology. For its characteristics, these repositories can be connected or disconnected.
Space does not permit a discussion of two types and we will see Disconnected
type in this article and we will leave Connected
type for future deliveries.
Set<TEntity> DbContext Method
It is a very important method in the Entity Framework Generic Repository construction. This method returns a reference to DbSet
of the type TEntity
within DbContext
.
In another words, Set<TEntity>
method, give us access to DbSet
of the TEntity
type, from a single DbContext
without we know, the DbSet
property name and without we know the specific DbContext
type.
More information can be found here.
I try to explain with code:
We have a simple DbContext GeneralEntities
with a simple DbSet Customers
of Customer
type:
public partial class GeneralEntities : DbContext
{
public GeneralEntities() : base("name=GeneralEntities") { }
public DbSet<Customer> Customers { get; set; }
}
We have created a simple method that has access to the Customers DbSet
:
public void Do(GeneralEntities context)
{
DbSet<Customer> myDbSet = context.Customers;
}
In a very simple case, because I know the DbContext
type, DbSet
name and the DbSet
is a static
type.
The next methods contain a generic dynamic instantiations of DbSet
:
public void Do(DbContext context)
{
DbSet<Customer> myDbSet = context.Set<Customer>();
}
public void Do<TEntity>(DbContext context) where TEntity : class
{
DbSet<TEntity> myDbSet = context.Set<TEntity>();
}
The methods parameters are DbContext
type (base class) and don’t have access to DbSet<Customer>
property directly.
Graphic comparison:
Example Classes
These are the example classes:
public partial class MyDBEntities : DbContext
{
public MyDBEntities()
: base("name=MyDBEntities")
{
}
public virtual DbSet<City> Cities { get; set; }
public virtual DbSet<FootballClub> FootballClubs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<City>()
.Property(e => e.Name)
.IsUnicode(false);
modelBuilder.Entity<FootballClub>()
.Property(e => e.Name)
.IsUnicode(false);
modelBuilder.Entity<FootballClub>()
.Property(e => e.Members)
.HasPrecision(18, 0);
}
}
public partial class City
{
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Column(TypeName = "numeric")]
public decimal? People { get; set; }
[Column(TypeName = "numeric")]
public decimal? Surface { get; set; }
public ICollection<FootballClub> FootballClubs { get; set; }
}
public partial class FootballClub
{
public int Id { get; set; }
public int CityId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Column(TypeName = "numeric")]
public decimal Members { get; set; }
[Required]
[StringLength(50)]
public string Stadium { get; set; }
[Column(TypeName = "date")]
public DateTime? FundationDate { get; set; }
public string Logo { get; set; }
}
Entity Framework Generic Repositories Disconnected
Entity Framework generic repository disconnected is used in stateless process as ASP.NET MVC, WebAPI, WPF/Forms disconnected approach, batch process, etc.
These repositories make the changes 1 to 1, and usually work with edition popups or new edit forms.
Its main characteristics are:
- Should receive the
Func<DbContext>
from dependency injection, because it will create a new DbContext
with each method execution. - It doesn’t need to have a
DbContext
property or it implements IDisposable
for the previous same case. - It isn’t necessary to have an
ObservableCollection<TEntity>
, because we will attack DbSet
directly. - It hasn’t a
SaveChanged
method, because in all methods, the data is saved. - If it has many clients open, it will consume few resources, because it interact only at the time of making the changes.
Building Entity Framework Generic Repositories Disconnected
In the first step, we will create the generic DesconGenericRepository
class:
public class DisconGenericRepository<TEntity> where TEntity : class
{
protected readonly Func<DbContext> _dbContextCreator;
public DesconGenericRepository(Func<DbContext> dbContextCreator)
{
if (dbContextCreator == null) throw new ArgumentNullException(nameof(dbContextCreator),
$"The parameter dbContextCreator can not be null");
_dbContextCreator = dbContextCreator;
}
}
The DisconGenericRepository
class has a constructor with a Func<DbContext>
parameter injected for dependency with a read only field corresponding.
The class has a generic constraint from reference types.
Let’s go to build all methods.
ALL / ALLASYNC
The All
/AllAsync
methods return the all
table data.
public IEnumerable<TEntity> All()
{
var result = Enumerable.Empty<TEntity>();
using(var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
result = dbSet.ToList();
}
return result;
}
public Task<IEnumerable<TEntity>> AllAsync()
{
return Task.Run(() =>
{
return All();
});
}
As we can see, we will open a using stamen, for instance a DbContext
with our Func<DbContext>
field helper. This will be a constant in all methods in disconnected generic repository class. We recover DbSet
instance and call your LinQ to Entities method ToList
, for executing the select
in this moment.
In use:
[TestMethod]
public void All_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
IEnumerable<FootballClub> result = instance.All();
Assert.IsNotNull(result);
Assert.IsTrue(result.Count() > 0);
}
FIND / FINDASYNC
The Find
/FindAsync
methods, is very similar to All
/AllAsync
methods, but Find
/FindAsync
searches for a simple row for PK. The PK can be simple or complex. Return one row always.
The Find
/FindAsync
methods are very similar to All
/AllAsync
methods, but Find
/FindAsync
searches a simple row for PK. The PK can be simple or complex. Return one row always.
public TEntity Find(params object[] pks)
{
if (pks == null) throw new ArgumentNullException(nameof(pks), $"The parameter pks can not be null");
TEntity result = null;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
result = dbSet.Find(pks);
}
return result;
}
public Task<TEntity> FindAsync(params object[] pks)
{
return Task.Run(() =>
{
return Find(pks);
});
}
The parameter pks
, is a params
parameter, so that accepts groups of values for complex PKs.
In use for simple pk:
[TestMethod]
public void Find_OK2()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
FootballClub result = instance.Find(1);
Assert.AreEqual(result.Id, 1);
}
In use for complex pk.
Table Definition:
[TestMethod]
public void Find_OK2()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
string propertyPk1 = "pk1";
int propertyPk2 = 15;
DateTime propertyPk3 = DateTime.Today;
FootballClub result = instance.Find(propertyPk1, propertyPk2, propertyPk3);
Assert.AreEqual(result.Id, 1);
}
GETDATA / GETDATAASYNC
Like Find
/FindAsync
, the methods GetData
/GetDataAsync
are very similar than All
/AllAsync
unlike, GetData
has an Expression<Func<TEntity,bool>>
parameter for filter the query.
public IEnumerable<TEntity> GetData(Expression<Func<TEntity, bool>> filter)
{
if (filter == null) throw new ArgumentNullException(nameof(filter),
$"The parameter filter can not be null");
var result = Enumerable.Empty<TEntity>();
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
result = dbSet.Where(filter).ToList();
}
return result;
}
public Task<IEnumerable<TEntity>> GetDataAsync(Expression<Func<TEntity, bool>> filter)
{
return Task.Run(() =>
{
return GetData(filter);
});
}
In use:
[TestMethod]
public void GetData_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
Expression<Func<FootballClub, bool>> filter = a => a.Name == "Real Madrid C. F.";
IEnumerable<FootballClub> result = instance.GetData(filter);
Assert.IsNotNull(result);
Assert.IsTrue(result.Count() == 1);
}
ADD / ADDASYNC
Add
/AddAsync
as their name suggests, make inserts elements in the database.
public int Add(TEntity newEntity)
{
if (newEntity == null) throw new ArgumentNullException(nameof(newEntity),
$"The parameter newEntity can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
dbSet.Add(newEntity);
result = context.SaveChanges();
}
return result;
}
public Task<int> AddAsync(TEntity newEntity)
{
return Task.Run(() =>
{
return Add(newEntity);
});
}
public int Add(IEnumerable<TEntity> newEntities)
{
if (newEntities == null) throw new ArgumentNullException(nameof(newEntities),
$"The parameter newEntities can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
dbSet.AddRange(newEntities);
result = context.SaveChanges();
}
return result;
}
public Task<int> AddAsync(IEnumerable<TEntity> newEntities)
{
return Task.Run(() =>
{
return Add(newEntities);
});
}
It has two overloads, for the single entity or a collection of entities, both return the number of element inserts in database.
In use:
[TestMethod]
public void Add_SimpleItem_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
FootballClub newEntity = new FootballClub
{
IdCity = 1,
Name = "New Team",
Members = 0,
Stadium = "New Stadium",
FundationDate = DateTime.Today
};
int result = instance.Add(newEntity);
int expected = 1;
Assert.AreEqual(expected, result);
}
[TestMethod]
public void Add_MultiItems_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
IEnumerable<FootballClub> newEntities = new List<FootballClub>
{
new FootballClub
{
IdCity = 1,
Name = "New Team",
Members = 0,
Stadium = "New Stadium",
FundationDate = DateTime.Today
},
new FootballClub
{
IdCity = 1,
Name = "New Team 2",
Members = 0,
Stadium = "New Stadium 2",
FundationDate = DateTime.Today
}
};
int result = instance.Add(newEntities);
int expected = 2;
Assert.AreEqual(expected, result);
}
REMOVE / REMOVEASYNC
These methods have more overloads and they are divided in two groups:
- Remove for Entity
- Remove for PKs
public int Remove(TEntity removeEntity)
{
if (removeEntity == null) throw new ArgumentNullException(nameof(removeEntity),
$"The parameter removeEntity can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
dbSet.Attach(removeEntity);
context.Entry(removeEntity).State = EntityState.Deleted;
result = context.SaveChanges();
}
return result;
}
public Task<int> RemoveAsync(TEntity removeEntity)
{
return Task.Run(() =>
{
return Remove(removeEntity);
});
}
public int Remove(IEnumerable<TEntity> removeEntities)
{
if (removeEntities == null) throw new ArgumentNullException(nameof(removeEntities),
$"The parameter removeEntities can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
foreach (var removeEntity in removeEntities)
{
dbSet.Attach(removeEntity);
context.Entry(removeEntity).State = EntityState.Deleted;
}
dbSet.RemoveRange(removeEntities);
result = context.SaveChanges();
}
return result;
}
public Task<int> RemoveAsync(IEnumerable<TEntity> removeEntities)
{
return Task.Run(() =>
{
return Remove(removeEntities);
});
}
public int Remove(params object[] pks)
{
if (pks == null) throw new ArgumentNullException(nameof(pks),
$"The parameter removeEntity can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
var entity = Find(pks);
dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Deleted;
result = context.SaveChanges();
}
return result;
}
public Task<int> RemoveAsync(params object[] pks)
{
return Task.Run(() =>
{
return Remove(pks);
});
}
For the removed methods, we have employed 2 important Entity Framework methods:
DbSet.Attach
- This DbSet
class method appends the entity object to the DbSet
property with the state unchanged. This is necessary because if we have used the DbSet.Remove
method, it would have raised an exception, because an entity that isn’t in the context (DbContext
) can’t be removed. DbContext.Entry(obj).State
- It consult the ChangeTracker DbContext
property and modifies its state to deleted.
In use:
[TestMethod]
public void Remove_SimpleItem_forEntity_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
var removeEntity = instance.Find(99);
int result = instance.Remove(removeEntity);
int expected = 0;
Assert.AreEqual(expected, result);
}
[TestMethod]
public void Remove_MultiItems_forEntity_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
IEnumerable<FootballClub> removeEntities = new List<FootballClub>
{
new FootballClub
{
Id = 9999,
CityId = 1,
Name = "New Team",
Members = 0,
Stadium = "New Stadium",
FundationDate = DateTime.Today
},
new FootballClub
{
Id = 100,
CityId = 1,
Name = "New Team 2",
Members = 0,
Stadium = "New Stadium 2",
FundationDate = DateTime.Today
}
};
int result = instance.Remove(removeEntities);
int expected = 0;
Assert.AreEqual(expected, result);
}
[TestMethod]
public void Remove_SimpleItem_forPK_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
int result = instance.Remove(pks: 9999);
int expected = 0;
Assert.AreEqual(expected, result);
}
UPDATE / UPDATEASYNC
Updated values in the database are very similar to Remove
methods, but more simple, because it doesn’t have update for PKs or for collections.
public int Update(TEntity updateEntity)
{
if (updateEntity == null) throw new ArgumentNullException(nameof(updateEntity),
$"The parameter updateEntity can not be null");
var result = 0;
using (var context = _dbContextCreator())
{
var dbSet = context.Set<TEntity>();
dbSet.Attach(updateEntity);
context.Entry(updateEntity).State = EntityState.Modified;
result = context.SaveChanges();
}
return result;
}
public Task<int> UpdateAsync(TEntity updateEntity)
{
return Task.Run(() =>
{
return Update(updateEntity);
});
}
In use:
[TestMethod]
public void Update_OK()
{
Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;
instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);
FootballClub updateEntity = new FootballClub
{
Id = 9999,
CityId = 1,
Name = "New Team 3",
Members = 10,
Stadium = "New Stadium 3",
FundationDate = DateTime.Today
};
int result = instance.Update(updateEntity);
int expected = 0;
Assert.AreEqual(expected, result);
}
Extracting the Interface
Once this has been done, we will extract the Interface.
Result:
public interface IDisconGenericRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> All();
Task<IEnumerable<TEntity>> AllAsync();
TEntity Find(params object[] pks);
Task<TEntity> FindAsync(params object[] pks);
IEnumerable<TEntity> GetData(Expression<Func<TEntity, bool>> filter);
Task<IEnumerable<TEntity>> GetDataAsync(Expression<Func<TEntity, bool>> filter);
int Add(TEntity newEntity);
Task<int> AddAsync(TEntity newEntity);
int Add(IEnumerable<TEntity> newEntities);
Task<int> AddAsync(IEnumerable<TEntity> newEntities);
int Remove(TEntity removeEntity);
Task<int> RemoveAsync(TEntity removeEntity);
int Remove(IEnumerable<TEntity> removeEntities);
Task<int> RemoveAsync(IEnumerable<TEntity> removeEntities);
int Remove(params object[] pks);
Task<int> RemoveAsync(params object[] pks);
int Update(TEntity updateEntity);
Task<int> UpdateAsync(TEntity updateEntity);
}
MVC Example
Let’s to try to use our Generic Repository with a ‘real application’, in this case ASP.NET MVC web application. Add a MVC project to our solution.
We will install Autofac.MVC
for Dependency Injection (IoC).
We will explain abstract concepts of Autofac.MVC
, for more information, check out this link.
In the Globalasax.cs class, we will add RegisterAutofac()
method and we will add its call in the first line in the Application_Start()
method.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
RegisterAutofac();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void RegisterAutofac()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterSource(new ViewRegistrationSource());
IDisconGenericRepository<FootballClub> footbalRepository =
new DisconGenericRepository<FootballClub>(() => new MyDBEntities());
builder.Register<IDisconGenericRepository<FootballClub>>(a => footbalRepository);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
Add a new complete Controller: FootballClubsController
, and generate all actions with its views.
We are going to look at the Controller
class:
public class FootballClubsController : Controller
{
private readonly IDisconGenericRepository<FootballClub> _repository;
public FootballClubsController(IDisconGenericRepository<FootballClub> repository)
{
_repository = repository;
}
}
Dependency injection of our disconnected Generic Repository.
These are the database Actions actions:
public ActionResult Index()
{
var model = _repository.All();
return View(model);
}
For Index
action, we will employ the All
repository method.
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FootballClub footballClub = _repository.Find(id);
if (footballClub == null)
{
return HttpNotFound();
}
return View(footballClub);
}
For Details
action, we will employ the Find
repository method for select the row by id.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,
CityId,Name,Members,Stadium,FundationDate,Logo")] FootballClub footballClub)
{
if (ModelState.IsValid)
{
_repository.Add(footballClub);
return RedirectToAction("Index");
}
return View(footballClub);
}
For Create post action, we will employ the Add
repository method for creating a new FootballClub
database row.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,CityId,Name,
Members,Stadium,FundationDate,Logo")] FootballClub footballClub)
{
if (ModelState.IsValid)
{
_repository.Update(footballClub);
return RedirectToAction("Index");
}
return View(footballClub);
}
For Edit post action, we will employ the Update
repository method for updating all properties of FootballClub
database row.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
_repository.Remove(id);
return RedirectToAction("Index");
}
For DeleteConfirmed
post action, we will employ the Remove
repository method for delete the FootballClub
database row by id. Remember that the Disconnected Generic Repository has a Remove
method for full FootballClub
object.
For more information of FootballClubController
, download the project.
In action:
https://www.youtube.com/watch?v=n_3mXMkYyw0&feature=youtu.be
WPF Example
Although WPF supports a state application convection (connected), we can use stateless technology and release database connections and resources.
The WPF application only connects to the database server for doing any action, and only for database action time, it is not connected in all application life cycle.
We have implemented the WPF project with a MVVM pattern. We use the next fantastic toolkits (more information):
In the following, we will show the classes (ViewModels
) where we use the Disconnected Generic Repository in the WPF project.
InsertViewModel
:
public class InsertViewModel : ViewModelBase
{
private FootballClub _model;
public FootballClub Model
{
get { return _model; }
set { Set(nameof(Model), ref _model, value); }
}
private readonly IDisconGenericRepository<FootballClub> _repository;
public InsertViewModel(FootballClub model, IDisconGenericRepository<FootballClub> repository)
{
Model = model;
_repository = repository;
}
public RelayCommand InsertCommand => new RelayCommand(InsertExecute);
private void InsertExecute()
{
_repository.Add(Model);
Messenger.Default.Send(new NotificationMessage("Inserted"));
}
}
We will call the Add
method in the InsertExecute
method of InsertCommand RelayCommand
.
EditViewModel
:
public class EditViewModel : ViewModelBase
{
private FootballClub _model;
public FootballClub Model
{
get { return _model; }
set { Set(nameof(Model), ref _model, value); }
}
private readonly IDisconGenericRepository<FootballClub> _repository;
public EditViewModel(FootballClub model, IDisconGenericRepository<FootballClub> repository)
{
Model = model;
_repository = repository;
}
public RelayCommand AceptChangesCommand => new RelayCommand(AceptChangesExecute);
private void AceptChangesExecute()
{
_repository.Update(Model);
Messenger.Default.Send(new NotificationMessage("Updated"));
}
public RelayCommand CancelCommand => new RelayCommand(CancelExecute);
private void CancelExecute()
{
Messenger.Default.Send(new NotificationMessage("Cancel"));
}
}
We will call the Update
method in the UpdateExecute
method of UpdateCommand RelayCommand
.
MainViewModel
:
public class MainViewModel : ViewModelBase
{
private readonly IDisconGenericRepository<FootballClub> _repository;
public ObservableCollection<FootballClub> Data { get; set; }
private FootballClub _selectedItem;
public FootballClub SelectedItem
{
get { return _selectedItem; }
set { Set(nameof(SelectedItem), ref _selectedItem, value); }
}
public MainViewModel(IDisconGenericRepository<FootballClub> repository)
{
_repository = repository;
Data = new ObservableCollection<FootballClub>(_repository.All());
}
public RelayCommand DeleteCommand => new RelayCommand(DeleteExecute, () => SelectedItem != null);
private void DeleteExecute()
{
_repository.Remove(SelectedItem);
Data.Remove(SelectedItem);
}
}
We will call the Remove
method in the DeleteExecute
method of DeleteCommand RelayCommand
.
https://www.youtube.com/watch?v=fyTYS6NgbVg&feature=youtu.be
Extending DisconGenericRepository<TEntity>
The DisconGenericRepository
has a few interesting methods, bud we may need to expand its functionality with news methods.
That meets our requirements.
The best way is inheriting the principal class and creating the new methods in derivates class.
public class FootballClubRepository : DisconGenericRepository<FootballClub>, IFootballClubRepository
{
public FootballClubRepository(Func<DbContext> dbContextCreator) : base(dbContextCreator) { }
public int UpdateRangeLow(IEnumerable<FootballClub> entities)
{
int result = 0;
foreach (var entity in entities)
{
result += base.Update(entity);
}
return result;
}
public int UpdateRangeFast(IEnumerable<FootballClub> entities)
{
int result = 0;
using(var context = base._dbContextCreator())
{
entities.ToList().ForEach(e => UpdateEntity(e, context));
result = context.SaveChanges();
}
return result;
}
private void UpdateEntity(FootballClub entity, DbContext context)
{
var dbSet = context.Set<FootballClub>();
dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
}
}
It isn’t common to make updates in block, but we decided to add this method because it will be useful.
For didactic reasons, we have inserted two methods, Update
, the first is a low method, because it will create a new connection to database for each update. The second method has better performance, because it executes the updates queries in the same database context. There is other private update
method that exists for refactoring reasons.
Test Project
The test project is comprised of five projects:
BuildingEFGRepository.DAL
- Contains the Repository Generics logic BuildingEFGRepository.DataBase
- Contains the Entity Framework classes, POCO database classes and custom Repositories BuildingEFGRepository.DataBase.Tests
- Contains the tests of BuildingEFGRepository.DataBase
BuildingEFGRepository.MVC
- Contains web ASP.NET MVC application BuildingEFGRepository.WPF_DesCon
- Contains WPF application
You need to change the connection string
for 3 Config.
We will change the original path, for our machine path:
Example:
C:\TFS\PakkkoTFS\Blog\C#\BuildingEFGRepository\BuildingEFGRepository.DataBase\MyDb.mdf
For:
C:\YourSolutionPath\BuildingEFGRepository\BuildingEFGRepository.DataBase\MyDb.mdf
Config file to change:
- BuildingEFGRepository.DAL.Tests\App.Config
- BuildingEFGRepository.MVC\Web.Config
- BuildingEFGRepository.WPF_DesCon\App.Config