Click here to Skip to main content
16,016,744 members
Articles / Web Development / HTML

Building Entity Framework Disconnected Generic Repository

Rate me:
Please Sign up or sign in to vote.
4.89/5 (9 votes)
26 Nov 2017CPOL7 min read 21.1K   351   27   4
Create an Entity Framework Generic Repository in a step by step manner

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.

Image 1

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:

C#
public partial class  GeneralEntities :  DbContext
{
    public GeneralEntities() : base("name=GeneralEntities") {  }
    public  DbSet<Customer>  Customers  { get; set; }
    /// More Code
} 

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

C#
public void Do(GeneralEntities context)
{
    /// We know the DbContext Type (GeneralEntities).
    /// The DbSet type is static.
    /// We know de DbSet name --> Customers.            
 
    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:

C#
public void Do(DbContext context)
{
    /// We don't know the DbContext Type, I know the base class.
    /// The DbSet type is static (Customer)
    /// We don't know the DbSet name --> Customers.
 
    DbSet<Customer> myDbSet = context.Set<Customer>();
}
 
public void Do<TEntity>(DbContext context) where TEntity : class
{
    /// We don't know the DbContext Type, I know the base class.
    /// The DbSet type is generic (TEntity)
    /// We don't know the DbSet name --> Customers.
 
    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:

Image 2

Example Classes

These are the example classes:

C#
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:

C#
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.

C#
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:

C#
[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.

C#
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:

C#
[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:

Image 3

C#
[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.

C#
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:

C#
[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.

C#
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:

C#
[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
C#
/// For Object (TEntity)

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);
    });
}

/// For PKs

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:

C#
[TestMethod]
public void Remove_SimpleItem_forEntity_OK()
{
    /// changed pk for tests
 
    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()
{
    /// changed pk for tests

    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()
{
    /// changed pk for tests

    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.

C#
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:

C#
[TestMethod]
public void Update_OK()
{
    /// changed values for tests

    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.

Image 4

Result:

C#
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.

Image 5

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

We will explain abstract concepts of Autofac.MVC, for more information, check out this link.

Image 6

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.

C#
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        /// Add call
        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());
 
        // manual registration of types;
        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:

C#
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:

C#
// GET: FootballClubs
public ActionResult Index()
{
    var model = _repository.All();
 
    return View(model);
}

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

C#
// GET: FootballClubs/Details/5
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.

C#
// POST: FootballClubs/Create
[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.

C#
// POST: FootballClubs/Edit/5
[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.

C#
// POST: FootballClubs/Delete/5
[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:

Image 7

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):

  • MVVM Light
  • MahApps

Image 8

In the following, we will show the classes (ViewModels) where we use the Disconnected Generic Repository in the WPF project.

InsertViewModel:

C#
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"));
    }
     
     // ... Another code
}

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

EditViewModel:

C#
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"));
    }
 
    // ... Another code 
}

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

MainViewModel:

C#
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());
    }

     // ... Another code 

    public RelayCommand DeleteCommand => new RelayCommand(DeleteExecute, () => SelectedItem != null);
 
    private void DeleteExecute()
    {
        _repository.Remove(SelectedItem);
 
        Data.Remove(SelectedItem);
    }
 
    // ... Another code
}

We will call the Remove method in the DeleteExecute method of DeleteCommand RelayCommand.

Image 9

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.

C#
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)
        {
            /// Is low, because create a connection foreach entity
            /// we use this case for didactic reasons
            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:

Image 10

  1. BuildingEFGRepository.DAL - Contains the Repository Generics logic
  2. BuildingEFGRepository.DataBase - Contains the Entity Framework classes, POCO database classes and custom Repositories
  3. BuildingEFGRepository.DataBase.Tests - Contains the tests of BuildingEFGRepository.DataBase
  4. BuildingEFGRepository.MVC - Contains web ASP.NET MVC application
  5. 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

Image 11

Config file to change:

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Cecabank
Spain Spain
MVP C# Corner 2017

MAP Microsoft Active Professional 2014

MCPD - Designing and Developing Windows Applications .NET Framework 4
MCTS - Windows Applications Development .NET Framework 4
MCTS - Accessing Data Development .NET Framework 4
MCTS - WCF Development .NET Framework 4

Comments and Discussions

 
QuestionSystem.Data.SqlClient.SqlException Cannot create file MyDB.mdf Pin
Anders Eriksson18-Dec-17 15:11
Anders Eriksson18-Dec-17 15:11 
AnswerRe: System.Data.SqlClient.SqlException Cannot create file MyDB.mdf Pin
Juan Francisco Morales Larios18-Dec-17 22:08
Juan Francisco Morales Larios18-Dec-17 22:08 
GeneralRe: System.Data.SqlClient.SqlException Cannot create file MyDB.mdf Pin
Anders Eriksson18-Dec-17 22:22
Anders Eriksson18-Dec-17 22:22 
GeneralMy vote of 5 Pin
Hyland Computer Systems27-Nov-17 15:04
Hyland Computer Systems27-Nov-17 15:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.