The repository pattern separates and retrieves the 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 the main group of equals methods, such as:
- All
- GetData
- Find
- Add
- Remove
- Update
All these classes have the very similar code and have very similar tests. Creating 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 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 return a reference to DbSet of the type TEntity within DbContext.
In other 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 info here.
I try to explain with code:
We have a simple DbContext GeneralEntities with a simple DbSet Customer 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 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, contains a generic dynamic instantiation 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 comparation
Example Classes
This is 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 have a DbContext property or it implements IDisposable for the previous same case.
- It isn’t necessary 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 only 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 constrain 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 execute 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 search 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 data base.
- 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:
- Attach .- This DbSet class method append 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 it can’t be removed a entity that isn’t in the context (DbContext).
- Entry(obj).State .- It consult the ChangeTracker DbContext property and modify 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. Is very similar to Remove methods, but is 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 info 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 the Controller class,
- public class FootballClubsController : Controller
- {
- private readonly IDisconGenericRepository<FootballClub> _repository;
-
-
- public FootballClubsController(IDisconGenericRepository<FootballClub> repository)
- {
- _repository = repository;
- }
- }
Dependency injection of our desconected 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 create 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 update 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 Desconected Generic Repository has a Remove method for full FotbalClub object.
For more info of FootballClubController download de project.
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 connect to database server for do any action, and only for database action time, it not connected in all application life cycle.
We have implemented the WPF project with a MVVM pattern. We use the next fantastic toolkits (more info):
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
For more info of WPF app download de project, and study the BuildingEFGRepository.WPF_Descon.
Extending DisconGenericRepository<TEntity>
The DisconGenericRepository has a few interesting methods, bud we may need to expand its functionality with news methods
That meet our requirements.
The best way is inheriting of the principal class and create 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 make updates in block, but we decided 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 connexon to database for each update. The second method has better performance, because execute the updates queries in the same database context. There is other private update method that exist for refactoring reasons.
Test Project
The test project is comprised for five projects,
- BuildingEFGRepository.DAL .- Contains the Repository Generics logic.
- DataBase .- Contains the Entitiy Framework classes, POCO database classes and custom Repositories.
- DataBase.Tests .- Contains the tests of BuildingEFGRepository.DataBase.
- MVC .- Contains web ASP.Net MVC application.
- WPF_DesCon .- Contains WPF application.
You need to change the connection string for 3 Config.
We will change 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
- <connectionStrings>
- <add name="MyDBEntities" connectionString="data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=C:\TFS\PakkkoTFS\Blog\C#\BuildingEFGRepository\BuildingEFGRepository.DataBase\MyDB.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
- </connectionStrings>
- <connectionStrings>
- <add name="MyDBEntities" connectionString="data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=C:\YourSolutionPath\BuildingEFGRepository\BuildingEFGRepository.DataBase\MyDB.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
- </connectionStrings>
Confing file to change,
- DAL.Tests\App.Config
- MVC\Web.Config
- WPF_DesCon\App.Config