Building Entity Framework Disconnected Generic Repository

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.
Entity Framework
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

  1. public partial class GeneralEntities: DbContext {  
  2.     public GeneralEntities(): base("name=GeneralEntities") {}  
  3.     public DbSet < Customer > Customers {  
  4.         get;  
  5.         set;  
  6.     }  
  7.     /// More Code  

We have created a simple method that access to the Customers DbSet

  1. public void Do(GeneralEntities context)  
  2. {  
  3.     /// We know the DbContext Type (GeneralEntities).  
  4.     /// The DbSet type is static.  
  5.     /// We know de DbSet name --> Customers.  
  6.     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.

  1. public void Do(DbContext context) {  
  2.     /// We don't know the DbContext Type, I know the base class.  
  3.     /// The DbSet type is static (Customer)  
  4.     /// We don't know the DbSet name --> Customers.  
  5.     DbSet < Customer > myDbSet = context.Set < Customer > ();  
  6. }  
  7. public void Do < TEntity > (DbContext context) where TEntity: class {  
  8.     /// We don't know the DbContext Type, I know the base class.  
  9.     /// The DbSet type is generic (TEntity)  
  10.     /// We don't know the DbSet name --> Customers.  
  11.     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

Entity Framework

Example Classes

This is the example classes

  1. public partial class MyDBEntities: DbContext {  
  2.     public MyDBEntities(): base("name=MyDBEntities") {}  
  3.     public virtual DbSet < City > Cities {  
  4.         get;  
  5.         set;  
  6.     }  
  7.     public virtual DbSet < FootballClub > FootballClubs {  
  8.         get;  
  9.         set;  
  10.     }  
  11.     protected override void OnModelCreating(DbModelBuilder modelBuilder) {  
  12.         modelBuilder.Entity < City > ().Property(e => e.Name).IsUnicode(false);  
  13.         modelBuilder.Entity < FootballClub > ().Property(e => e.Name).IsUnicode(false);  
  14.         modelBuilder.Entity < FootballClub > ().Property(e => e.Members).HasPrecision(18, 0);  
  15.     }  
  16. }  
  17. public partial class City {  
  18.     public int Id {  
  19.         get;  
  20.         set;  
  21.     }  
  22.     [Required]  
  23.     [StringLength(50)]  
  24.     public string Name {  
  25.         get;  
  26.         set;  
  27.     }  
  28.     [Column(TypeName = "numeric")]  
  29.     public decimal ? People {  
  30.             get;  
  31.             set;  
  32.         }  
  33.         [Column(TypeName = "numeric")]  
  34.     public decimal ? Surface {  
  35.         get;  
  36.         set;  
  37.     }  
  38.     public ICollection < FootballClub > FootballClubs {  
  39.         get;  
  40.         set;  
  41.     }  
  42. }  
  43. public partial class FootballClub {  
  44.     public int Id {  
  45.         get;  
  46.         set;  
  47.     }  
  48.     public int CityId {  
  49.         get;  
  50.         set;  
  51.     }  
  52.     [Required]  
  53.     [StringLength(50)]  
  54.     public string Name {  
  55.         get;  
  56.         set;  
  57.     }  
  58.     [Column(TypeName = "numeric")]  
  59.     public decimal Members {  
  60.         get;  
  61.         set;  
  62.     }  
  63.     [Required]  
  64.     [StringLength(50)]  
  65.     public string Stadium {  
  66.         get;  
  67.         set;  
  68.     }  
  69.     [Column(TypeName = "date")]  
  70.     public DateTime ? FundationDate {  
  71.         get;  
  72.         set;  
  73.     }  
  74.     public string Logo {  
  75.         get;  
  76.         set;  
  77.     }  
  78. }  

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

  1. public class DisconGenericRepository < TEntity > where TEntity: class {  
  2.     protected readonly Func < DbContext > _dbContextCreator;  
  3.     public DesconGenericRepository(Func < DbContext > dbContextCreator) {  
  4.         if (dbContextCreator == nullthrow new ArgumentNullException(nameof(dbContextCreator), $ "The parameter dbContextCreator can not be null");  
  5.         _dbContextCreator = dbContextCreator;  
  6.     }  

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.

  1. public IEnumerable < TEntity > All() {  
  2.     var result = Enumerable.Empty < TEntity > ();  
  3.     using(var context = _dbContextCreator()) {  
  4.         var dbSet = context.Set < TEntity > ();  
  5.         result = dbSet.ToList();  
  6.     }  
  7.     return result;  
  8. }  
  9. public Task < IEnumerable < TEntity >> AllAsync() {  
  10.     return Task.Run(() => {  
  11.         return All();  
  12.     });  
  13. }  

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

  1. [TestMethod]  
  2. public void All_OK() {  
  3.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  4.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  5.     IEnumerable < FootballClub > result = instance.All();  
  6.     Assert.IsNotNull(result);  
  7.     Assert.IsTrue(result.Count() > 0);  
  8. }  

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.

  1. public TEntity Find(params object[] pks) {  
  2.     if (pks == nullthrow new ArgumentNullException(nameof(pks), $ "The parameter pks can not be null");  
  3.     TEntity result = null;  
  4.     using(var context = _dbContextCreator()) {  
  5.         var dbSet = context.Set < TEntity > ();  
  6.         result = dbSet.Find(pks);  
  7.     }  
  8.     return result;  
  9. }  
  10. public Task < TEntity > FindAsync(params object[] pks) {  
  11.     return Task.Run(() => {  
  12.         return Find(pks);  
  13.     });  
  14. }  

The parameter pks, is a params parameter, so that accepts groups of values for complex PKs.

In use for simple pk,

  1. [TestMethod]  
  2. public void Find_OK2() {  
  3.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  4.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  5.     FootballClub result = instance.Find(1);  
  6.     Assert.AreEqual(result.Id, 1);  

In use for complex pk.

Table Definition

Entity Framework

  1. [TestMethod]  
  2. public void Find_OK2() {  
  3.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  4.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  5.     string propertyPk1 = "pk1";  
  6.     int propertyPk2 = 15;  
  7.     DateTime propertyPk3 = DateTime.Today;  
  8.     FootballClub result = instance.Find(propertyPk1, propertyPk2, propertyPk3);  
  9.     Assert.AreEqual(result.Id, 1);  
  10. }  

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.

  1. public IEnumerable < TEntity > GetData(Expression < Func < TEntity, bool >> filter) {  
  2.     if (filter == nullthrow new ArgumentNullException(nameof(filter), $ "The parameter filter can not be null");  
  3.     var result = Enumerable.Empty < TEntity > ();  
  4.     using(var context = _dbContextCreator()) {  
  5.         var dbSet = context.Set < TEntity > ();  
  6.         result = dbSet.Where(filter).ToList();  
  7.     }  
  8.     return result;  
  9. }  
  10. public Task < IEnumerable < TEntity >> GetDataAsync(Expression < Func < TEntity, bool >> filter) {  
  11.     return Task.Run(() => {  
  12.         return GetData(filter);  
  13.     });  

In use,

  1. [TestMethod]  
  2. public void GetData_OK() {  
  3.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  4.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  5.     Expression < Func < FootballClub, bool >> filter = a => a.Name == "Real Madrid C. F.";  
  6.     IEnumerable < FootballClub > result = instance.GetData(filter);  
  7.     Assert.IsNotNull(result);  
  8.     Assert.IsTrue(result.Count() == 1);  
  9. }  

Add / AddAsync

Add/AddAsync as their name suggests, make inserts elements in the data base.

  1. public int Add(TEntity newEntity) {  
  2.     if (newEntity == nullthrow new ArgumentNullException(nameof(newEntity), $ "The parameter newEntity can not be null");  
  3.     var result = 0;  
  4.     using(var context = _dbContextCreator()) {  
  5.         var dbSet = context.Set < TEntity > ();  
  6.         dbSet.Add(newEntity);  
  7.         result = context.SaveChanges();  
  8.     }  
  9.     return result;  
  10. }  
  11. public Task < int > AddAsync(TEntity newEntity) {  
  12.     return Task.Run(() => {  
  13.         return Add(newEntity);  
  14.     });  
  15. }  
  16. public int Add(IEnumerable < TEntity > newEntities) {  
  17.     if (newEntities == nullthrow new ArgumentNullException(nameof(newEntities), $ "The parameter newEntities can not be null");  
  18.     var result = 0;  
  19.     using(var context = _dbContextCreator()) {  
  20.         var dbSet = context.Set < TEntity > ();  
  21.         dbSet.AddRange(newEntities);  
  22.         result = context.SaveChanges();  
  23.     }  
  24.     return result;  
  25. }  
  26. public Task < int > AddAsync(IEnumerable < TEntity > newEntities) {  
  27.     return Task.Run(() => {  
  28.         return Add(newEntities);  
  29.     });  
  30. }  

It has two overloads, for the single entity or a collection of entities, both return the number of element inserts in database.

In use

  1. [TestMethod]  
  2. public void Add_SimpleItem_OK() {  
  3.         Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  4.         instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  5.         FootballClub newEntity = new FootballClub {  
  6.             IdCity = 1,  
  7.                 Name = "New Team",  
  8.                 Members = 0,  
  9.                 Stadium = "New Stadium",  
  10.                 FundationDate = DateTime.Today  
  11.         };  
  12.         int result = instance.Add(newEntity);  
  13.         int expected = 1;  
  14.         Assert.AreEqual(expected, result);  
  15.     }  
  16.     [TestMethod]  
  17. public void Add_MultiItems_OK() {  
  18.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  19.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  20.     IEnumerable < FootballClub > newEntities = new List < FootballClub > {  
  21.         new FootballClub {  
  22.             IdCity = 1,  
  23.                 Name = "New Team",  
  24.                 Members = 0,  
  25.                 Stadium = "New Stadium",  
  26.                 FundationDate = DateTime.Today  
  27.         },  
  28.         new FootballClub {  
  29.             IdCity = 1,  
  30.                 Name = "New Team 2",  
  31.                 Members = 0,  
  32.                 Stadium = "New Stadium 2",  
  33.                 FundationDate = DateTime.Today  
  34.         }  
  35.     };  
  36.     int result = instance.Add(newEntities);  
  37.     int expected = 2;  
  38.     Assert.AreEqual(expected, result);  
  39. }  

Remove / RemoveAsync

These methods have more overloads and they are divided in two groups,

  • Remove for Entity.
  • Remove for PKs.

  1. /// For Object (TEntity)  
  2. public int Remove(TEntity removeEntity) {  
  3.     if (removeEntity == nullthrow new ArgumentNullException(nameof(removeEntity), $ "The parameter removeEntity can not be null");  
  4.     var result = 0;  
  5.     using(var context = _dbContextCreator()) {  
  6.         var dbSet = context.Set < TEntity > ();  
  7.         dbSet.Attach(removeEntity);  
  8.         context.Entry(removeEntity).State = EntityState.Deleted;  
  9.         result = context.SaveChanges();  
  10.     }  
  11.     return result;  
  12. }  
  13. public Task < int > RemoveAsync(TEntity removeEntity) {  
  14.     return Task.Run(() => {  
  15.         return Remove(removeEntity);  
  16.     });  
  17. }  
  18. public int Remove(IEnumerable < TEntity > removeEntities) {  
  19.     if (removeEntities == nullthrow new ArgumentNullException(nameof(removeEntities), $ "The parameter removeEntities can not be null");  
  20.     var result = 0;  
  21.     using(var context = _dbContextCreator()) {  
  22.         var dbSet = context.Set < TEntity > ();  
  23.         foreach(var removeEntity in removeEntities) {  
  24.             dbSet.Attach(removeEntity);  
  25.             context.Entry(removeEntity).State = EntityState.Deleted;  
  26.         }  
  27.         dbSet.RemoveRange(removeEntities);  
  28.         result = context.SaveChanges();  
  29.     }  
  30.     return result;  
  31. }  
  32. public Task < int > RemoveAsync(IEnumerable < TEntity > removeEntities) {  
  33.     return Task.Run(() => {  
  34.         return Remove(removeEntities);  
  35.     });  
  36. }  
  37. /// For PKs  
  38. public int Remove(params object[] pks) {  
  39.     if (pks == nullthrow new ArgumentNullException(nameof(pks), $ "The parameter removeEntity can not be null");  
  40.     var result = 0;  
  41.     using(var context = _dbContextCreator()) {  
  42.         var dbSet = context.Set < TEntity > ();  
  43.         var entity = Find(pks);  
  44.         dbSet.Attach(entity);  
  45.         context.Entry(entity).State = EntityState.Deleted;  
  46.         result = context.SaveChanges();  
  47.     }  
  48.     return result;  
  49. }  
  50. public Task < int > RemoveAsync(params object[] pks) {  
  51.     return Task.Run(() => {  
  52.         return Remove(pks);  
  53.     });  
  54. }  

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,

  1. [TestMethod]  
  2. public void Remove_SimpleItem_forEntity_OK() {  
  3.         /// changed pk for tests  
  4.         Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  5.         instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  6.         var removeEntity = instance.Find(99);  
  7.         int result = instance.Remove(removeEntity);  
  8.         int expected = 0;  
  9.         Assert.AreEqual(expected, result);  
  10.     }  
  11.     [TestMethod]  
  12. public void Remove_MultiItems_forEntity_OK() {  
  13.         /// changed pk for tests  
  14.         Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  15.         instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  16.         IEnumerable < FootballClub > removeEntities = new List < FootballClub > {  
  17.             new FootballClub {  
  18.                 Id = 9999,  
  19.                     CityId = 1,  
  20.                     Name = "New Team",  
  21.                     Members = 0,  
  22.                     Stadium = "New Stadium",  
  23.                     FundationDate = DateTime.Today  
  24.             },  
  25.             new FootballClub {  
  26.                 Id = 100,  
  27.                     CityId = 1,  
  28.                     Name = "New Team 2",  
  29.                     Members = 0,  
  30.                     Stadium = "New Stadium 2",  
  31.                     FundationDate = DateTime.Today  
  32.             }  
  33.         };  
  34.         int result = instance.Remove(removeEntities);  
  35.         int expected = 0;  
  36.         Assert.AreEqual(expected, result);  
  37.     }  
  38.     [TestMethod]  
  39. public void Remove_SimpleItem_forPK_OK() {  
  40.     /// changed pk for tests  
  41.     Func < DbContext > contextCreator = () => new MyDBEntities() as DbContext;  
  42.     instance = new DisconGenericRepository < FootballClub > (dbContextCreator: contextCreator);  
  43.     int result = instance.Remove(pks: 9999);  
  44.     int expected = 0;  
  45.     Assert.AreEqual(expected, result);  
  46. }  

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.

  1. public int Update(TEntity updateEntity) {  
  2.     if (updateEntity == nullthrow new ArgumentNullException(nameof(updateEntity), $ "The parameter updateEntity can not be null");  
  3.     var result = 0;  
  4.     using(var context = _dbContextCreator()) {  
  5.         var dbSet = context.Set < TEntity > ();  
  6.         dbSet.Attach(updateEntity);  
  7.         context.Entry(updateEntity).State = EntityState.Modified;  
  8.         result = context.SaveChanges();  
  9.     }  
  10.     return result;  
  11. }  
  12. public Task < int > UpdateAsync(TEntity updateEntity) {  
  13.     return Task.Run(() => {  
  14.         return Update(updateEntity);  
  15.     });  
  16. }  

In use, 

  1. [TestMethod]  
  2. public void Update_OK()  
  3. {  
  4.     /// changed values for tests  
  5.   
  6.     Func<DbContext> contextCreator = () => new MyDBEntities() as DbContext;  
  7.    
  8.     instance = new DisconGenericRepository<FootballClub>(dbContextCreator: contextCreator);  
  9.    
  10.     FootballClub updateEntity = new FootballClub  
  11.     {  
  12.         Id            = 9999,  
  13.         CityId        = 1,  
  14.         Name          = "New Team 3",  
  15.         Members       = 10,  
  16.         Stadium       = "New Stadium 3",  
  17.         FundationDate = DateTime.Today  
  18.     };  
  19.    
  20.     int result = instance.Update(updateEntity);  
  21.     int expected = 0;  
  22.    
  23.     Assert.AreEqual(expected, result);  
  24. }  

Extracting the Interface

Once this has been done, we will extract the Interface.

Entity Framework

Result

  1. public interface IDisconGenericRepository<TEntity> where TEntity : class  
  2. {  
  3.     IEnumerable<TEntity> All();  
  4.     Task<IEnumerable<TEntity>> AllAsync();  
  5.     TEntity Find(params object[] pks);  
  6.     Task<TEntity> FindAsync(params object[] pks);  
  7.     IEnumerable<TEntity> GetData(Expression<Func<TEntity, bool>> filter);  
  8.     Task<IEnumerable<TEntity>> GetDataAsync(Expression<Func<TEntity, bool>> filter);  
  9.     int Add(TEntity newEntity);  
  10.     Task<int> AddAsync(TEntity newEntity);  
  11.     int Add(IEnumerable<TEntity> newEntities);  
  12.     Task<int> AddAsync(IEnumerable<TEntity> newEntities);  
  13.     int Remove(TEntity removeEntity);  
  14.     Task<int> RemoveAsync(TEntity removeEntity);  
  15.     int Remove(IEnumerable<TEntity> removeEntities);  
  16.     Task<int> RemoveAsync(IEnumerable<TEntity> removeEntities);  
  17.     int Remove(params object[] pks);  
  18.     Task<int> RemoveAsync(params object[] pks);  
  19.     int Update(TEntity updateEntity);  
  20.     Task<int> UpdateAsync(TEntity updateEntity);  
  21. }  

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.

Entity Framework

We will install Autofac.MVC for Dependency Injection (IoC).

We will explain abstract concepts of Autofac.MVC, for more info link.

Entity Framework

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.

  1. public class MvcApplication : System.Web.HttpApplication  
  2. {  
  3.     protected void Application_Start()  
  4.     {  
  5.         /// Add call  
  6.         RegisterAutofac();  
  7.    
  8.         AreaRegistration.RegisterAllAreas();  
  9.         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
  10.         RouteConfig.RegisterRoutes(RouteTable.Routes);  
  11.         BundleConfig.RegisterBundles(BundleTable.Bundles);  
  12.     }  
  13.    
  14.    
  15.     private void RegisterAutofac()  
  16.     {  
  17.         var builder = new ContainerBuilder();  
  18.         builder.RegisterControllers(Assembly.GetExecutingAssembly());  
  19.         builder.RegisterSource(new ViewRegistrationSource());  
  20.    
  21.         // manual registration of types;  
  22.         IDisconGenericRepository<FootballClub> footbalRepository = new DisconGenericRepository<FootballClub>(() => new MyDBEntities());  
  23.         builder.Register<IDisconGenericRepository<FootballClub>>(a => footbalRepository);  
  24.    
  25.    
  26.         var container = builder.Build();  
  27.    
  28.         DependencyResolver.SetResolver(new AutofacDependencyResolver(container));  
  29.     }  
  30. }  

Add a new complete Controller: FootballClubsController, and generate all actions with its views.

We are going to look the Controller class,

  1. public class FootballClubsController : Controller  
  2. {  
  3.     private readonly IDisconGenericRepository<FootballClub> _repository;  
  4.    
  5.    
  6.     public FootballClubsController(IDisconGenericRepository<FootballClub> repository)  
  7.     {  
  8.         _repository = repository;  
  9.     }  
  10.  }  

Dependency injection of our desconected Generic Repository.

These are the database Actions actions,

  1. // GET: FootballClubs  
  2. public ActionResult Index()  
  3. {  
  4.     var model = _repository.All();  
  5.    
  6.     return View(model);  
  7. }  

For Index action, we will employ the All repository method.

  1. // GET: FootballClubs/Details/5  
  2. public ActionResult Details(int? id)  
  3. {  
  4.     if (id == null)  
  5.     {  
  6.         return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  7.     }  
  8.     FootballClub footballClub = _repository.Find(id);  
  9.     if (footballClub == null)  
  10.     {  
  11.         return HttpNotFound();  
  12.     }  
  13.     return View(footballClub);  
  14. }  

For Details action, we will employ the Find repository method for select the row by id.

  1. // POST: FootballClubs/Create  
  2. [HttpPost]  
  3. [ValidateAntiForgeryToken]  
  4. public ActionResult Create([Bind(Include = "Id,CityId,Name,Members,Stadium,FundationDate,Logo")] FootballClub footballClub)  
  5. {  
  6.     if (ModelState.IsValid)  
  7.     {  
  8.         _repository.Add(footballClub);  
  9.         return RedirectToAction("Index");  
  10.     }  
  11.    
  12.     return View(footballClub);  

For Create post action, we will employ the Add repository method for create a new FootballClub database row.

  1. // POST: FootballClubs/Edit/5  
  2. [HttpPost]  
  3. [ValidateAntiForgeryToken]  
  4. public ActionResult Edit([Bind(Include = "Id,CityId,Name,Members,Stadium,FundationDate,Logo")] FootballClub footballClub) {  
  5.     if (ModelState.IsValid) {  
  6.         _repository.Update(footballClub);  
  7.         return RedirectToAction("Index");  
  8.     }  
  9.     return View(footballClub);  

For Edit post action, we will employ the Update repository method for update all properties of FootballClub database row.

  1. // POST: FootballClubs/Delete/5  
  2. [HttpPost, ActionName("Delete")]  
  3. [ValidateAntiForgeryToken]  
  4. public ActionResult DeleteConfirmed(int id)  
  5. {  
  6.     _repository.Remove(id);  
  7.     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

  1. public class InsertViewModel: ViewModelBase {  
  2.     private FootballClub _model;  
  3.     public FootballClub Model {  
  4.         get {  
  5.             return _model;  
  6.         }  
  7.         set {  
  8.             Set(nameof(Model), ref _model, value);  
  9.         }  
  10.     }  
  11.     private readonly IDisconGenericRepository < FootballClub > _repository;  
  12.     public InsertViewModel(FootballClub model, IDisconGenericRepository < FootballClub > repository) {  
  13.         Model = model;  
  14.         _repository = repository;  
  15.     }  
  16.     public RelayCommand InsertCommand => new RelayCommand(InsertExecute);  
  17.     private void InsertExecute() {  
  18.         _repository.Add(Model);  
  19.         Messenger.Default.Send(new NotificationMessage("Inserted"));  
  20.     }  
  21.     // ... Another code  
  22. }  

We will call the Add method in the InsertExecute method of InsertCommand RelayCommand.

EditViewModel

  1. public class EditViewModel: ViewModelBase {  
  2.     private FootballClub _model;  
  3.     public FootballClub Model {  
  4.         get {  
  5.             return _model;  
  6.         }  
  7.         set {  
  8.             Set(nameof(Model), ref _model, value);  
  9.         }  
  10.     }  
  11.     private readonly IDisconGenericRepository < FootballClub > _repository;  
  12.     public EditViewModel(FootballClub model, IDisconGenericRepository < FootballClub > repository) {  
  13.         Model = model;  
  14.         _repository = repository;  
  15.     }  
  16.     public RelayCommand AceptChangesCommand => new RelayCommand(AceptChangesExecute);  
  17.     private void AceptChangesExecute() {  
  18.         _repository.Update(Model);  
  19.         Messenger.Default.Send(new NotificationMessage("Updated"));  
  20.     }  
  21.     public RelayCommand CancelCommand => new RelayCommand(CancelExecute);  
  22.     private void CancelExecute() {  
  23.         Messenger.Default.Send(new NotificationMessage("Cancel"));  
  24.     }  
  25.     // ... Another code  
  26. }  

We will call the Update method in the UpdateExecute method of UpdateCommand RelayCommand.

MainViewModel

  1. public class MainViewModel: ViewModelBase {  
  2.     private readonly IDisconGenericRepository < FootballClub > _repository;  
  3.     public ObservableCollection < FootballClub > Data {  
  4.         get;  
  5.         set;  
  6.     }  
  7.     private FootballClub _selectedItem;  
  8.     public FootballClub SelectedItem {  
  9.         get {  
  10.             return _selectedItem;  
  11.         }  
  12.         set {  
  13.             Set(nameof(SelectedItem), ref _selectedItem, value);  
  14.         }  
  15.     }  
  16.     public MainViewModel(IDisconGenericRepository < FootballClub > repository) {  
  17.         _repository = repository;  
  18.         Data = new ObservableCollection < FootballClub > (_repository.All());  
  19.     }  
  20.     // ... Another code  
  21.     public RelayCommand DeleteCommand => new RelayCommand(DeleteExecute, () => SelectedItem != null);  
  22.     private void DeleteExecute() {  
  23.         _repository.Remove(SelectedItem);  
  24.         Data.Remove(SelectedItem);  
  25.     }  
  26.     // ... Another code  

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.

  1. public class FootballClubRepository: DisconGenericRepository < FootballClub > , IFootballClubRepository {  
  2.     public FootballClubRepository(Func < DbContext > dbContextCreator): base(dbContextCreator) {}  
  3.     public int UpdateRangeLow(IEnumerable < FootballClub > entities) {  
  4.         int result = 0;  
  5.         foreach(var entity in entities) {  
  6.             /// Is low, because create a conexion foreach entity  
  7.             /// we use this case for didactic reasons  
  8.             result += base.Update(entity);  
  9.         }  
  10.         return result;  
  11.     }  
  12.     public int UpdateRangeFast(IEnumerable < FootballClub > entities) {  
  13.         int result = 0;  
  14.         using(var context = base._dbContextCreator()) {  
  15.             entities.ToList().ForEach(e => UpdateEntity(e, context));  
  16.             result = context.SaveChanges();  
  17.         }  
  18.         return result;  
  19.     }  
  20.     private void UpdateEntity(FootballClub entity, DbContext context) {  
  21.         var dbSet = context.Set < FootballClub > ();  
  22.         dbSet.Attach(entity);  
  23.         context.Entry(entity).State = EntityState.Modified;  
  24.     }  

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,

Entity Framework
  • 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

  1. <connectionStrings>  
  2.     <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" />   
  3. </connectionStrings>  
  4. <connectionStrings>  
  5.     <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" />   
  6. </connectionStrings> 

Confing file to change,

  1. DAL.Tests\App.Config
  2. MVC\Web.Config
  3. WPF_DesCon\App.Config


Similar Articles