Skip to main content
2 of 2
Added repository pattern and unit of work example.
Esteban
  • 111
  • 4

This seems about right.

There is only one thing I'd like to point out.

I'd change this:

public interface IEntity {
    int Id { get; }
}

public class Entity : IEntity {
    public virtual int Id { get; set; }
}

All your entities are assumed to have only one Id, ruling out every composite key.

Let's think about this common scenario:

User.cs

public class User {
    public int Id { get; set; }
    public string Username { get; set; }
}

Email.cs

public class Email {
    public int Id { get; set; }
    public string Address { get; set; }
}

UserEmail.cs

public class UserEmail {
    public int UserId { get; set; }
    public int EmailId { get; set; }
    public bool IsPrimary { get; set; } //Adding this prop requires to map this entity.
}

Given your base entity and entity interface you won't be able to handle composite keys. Also, keep in mind that Entity Framework will handle by itself the many to many relationships as long as the table contains no more than the Id's, when you add any other property you will have to add that entity to your project.

My advice is to move the Id to every entity, if you need/want to keep your base entity interface and class as a constraint, you could, but you dont really need the interface and the class.

Like:

public interface IEntity {
    int Id { get; }
}

public class Entity : IEntity {
    public virtual int Id { get; set; }
}

public class User : Entity, IUser {
    public int Id { get; set; }
    // ...
}

##EDIT:##

###IProvider.cs###

internal interface IProvider {
    MyContext DbContext { get; set; }

    IRepository<T> GetGenericRepository<T>()
        where T : class;

    T GetCustomRepository<T>(Func<MyContext, object> factory = null)
        where T : class;
}

###Provider.cs###

internal class Provider : IProvider {
    private readonly Factory _factory;
    protected Dictionary<Type, object> Repositories { get; private set; }
    public MyContext DbContext { get; set; }

    internal Provider() {
        _factory = new Factory();
        Repositories = new Dictionary<Type, object>(); }

    protected virtual T MakeRepository<T>(
        Func<MyContext, object> factory, MyContext dbContext) {
        var f = factory ?? _factory.GetRepositoryFactory<T>();
        if (f == null) throw new NotSupportedException(typeof(T).FullName);
        var repo = (T)f(dbContext);
        Repositories[typeof(T)] = repo;
        return repo; }

    public IRepository<T> GetGenericRepository<T>() where T : class {
        return GetCustomRepository<IRepository<T>>(
            _factory.GetRepositoryFactoryForEntityType<T>()); }

    public virtual T GetCustomRepository<T>(Func<MyContext, object> factory = null)
        where T : class {
        object repoObj;
        Repositories.TryGetValue(typeof(T), out repoObj);
        if (repoObj != null) { return (T)repoObj; }
        return MakeRepository<T>(factory, DbContext); }

    public void SetRepository<T>(T repository) {
        Repositories[typeof(T)] = repository; }
}

###Factory.cs###

internal class Factory {
    private readonly IDictionary<Type, Func<MyContext, object>> _factories;

    public Factory() { _factories = GetFactories(); }

    public Factory(IDictionary<Type, Func<MyContext, object>> factories) {
        _factories = factories; }

    private IDictionary<Type, Func<MyContext, object>> GetFactories() {
        return new Dictionary<Type, Func<MyContext, object>>
            { { typeof(ILogRepository),
                    context => new LogRepository(context) },
                { typeof(IExtendedRepository),
                    context => new ExtendedRepository(context) } }; }

    protected virtual Func<MyContext, object> DefaultEntityRepositoryFactory<T>()
        where T : class {
        return dbContext => new Repository<T>(dbContext); }

    public Func<MyContext, object> GetRepositoryFactory<T>() {
        Func<MyContext, object> factory;
        _factories.TryGetValue(typeof(T), out factory);
        return factory; }

    public Func<MyContext, object> GetRepositoryFactoryForEntityType<T>()
        where T : class {
        return GetRepositoryFactory<T>() ?? DefaultEntityRepositoryFactory<T>(); } }

###IRepository.cs###

public interface IRepository<T> : IDisposable
    where T : class
{
    IList<T> All();
    IList<T> Find(Func<T, bool> f);
    T Add(T t);
    T Update(T t);
    T Remove(T t);
}

###Repository.cs###

internal class Repository<T> : IRepository<T> where T : class
{
    internal MyContext Context;

    public Repository(MyContext context) { Context = context; }

    public IList<T> All() { return Context.Set<T>().ToList(); }

    public IList<T> Find(Func<T, bool> f) {
        return Context.Set<T>().Where(f).ToList();
    }

    public T Add(T t)  { /* add but don't save */ }

    public T Update(T t) { /* update but don't save */ }

    public T Remove(T t) { /* remove but don't save */ }

    public void Dispose() { /* dispose context */  }
}

###ILogRepository.cs###

public interface ILogRepository : IDisposable
{
    IList<Log> All();
    IList<Log> Find(Func<Log, bool> f);
}

###LogRepository.cs###

internal class LogRepository : ILogRepository
{
    internal DepofisContext Context;

    public LogRepository(DepofisContext context)
    {
        Context = context;
    }

    public IList<Log> All()
    {
        return Context.Logs.ToList();
    }

    public IList<Log> Find(Func<Log, bool> f)
    {
        return Context.Logs.Where(f).ToList();
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

###IExtendedRepository.cs###

public interface IExtendedRepository : IRepository<Extend>
{
    Extend NewMethod(Extend t);
}

###ExtendedRepository.cs###

internal class ExtendedRepository : Repository<Extend>
{
    public ExtendedRepository(MyContext context)
        : base(context) { }

    public Extend NewMethod(Extend t)
    {
        return t;
    }
}

###IUnitOfWork.cs###

internal interface IUnitOfWork : IDisposable
{
    int Save();
}

###UnitOfWork.cs###

public class UnitOfWork : IUnitOfWork
{
    private MyContext Context { get; set; }
    internal IProvider Provider { get; set; }

    private IRepository<T> GetGenericRepository<T>() where T : class {
        return Provider.GetGenericRepository<T>();
    }
    private T GetCustomRepository<T>() where T : class {
        return Provider.GetCustomRepository<T>(); 
    }
    private void CreateContext() { Context = new MyContext(); }

    public UnitOfWork() {
        CreateContext();
        if (Provider == null) Provider = new Provider();
        Provider.DbContext = Context;
    }

    public IRepository<User> Users { 
        get { return GetGenericRepository<User>(); } }
    public IRepository<Role> Roles { 
        get { return GetGenericRepository<Role>(); } }
    public ILogRepository Logs { 
        get { return GetCustomRepository<ILogRepository>(); } }
    public IExtendedRepository Extends { 
        get { return GetCustomRepository<IExtendedRepository>(); } }

    public int Save() { return Context.SaveChanges(); }
    public void Dispose() { /* dispose context */ }
}

As for the usage:

using(var uow = new UnitOfWork()) {
    var users = uow.Users.All();

    // do magic stuff here ;)

    uow.Save(); // don't forget to save
}

Now the uow have these methods:

  • uow.Users
    • .All()
    • .Find(q=>q.UserId == 1)
    • .Add(new User())
    • .Update(user)
    • .Remove(user)
  • uow.Logs
    • .All()
    • .Find(q=>q.LogId == 1)
  • uow.Extends
    • Same methods as uow.Users
    • .NewMethod(extend)
Esteban
  • 111
  • 4