Entity Framework 4.1 Generic Repository set up

你。 提交于 2019-12-01 13:24:50

问题


I'm new to Entity Framework and joined a project that uses a generic repository as shown below. Are there any disadvantages to the way the repository is set up? I've noticed that most tutorials describe creating multiple repositories based on a generic base repository rather than having one generic repository that handles everything.

To give some background this code is part of an ASP.NET MVC 3 website and we're using unity as an IOC container. All of the components in the business layer inherit from a base class that has has the IEntityRepository injected via the constructor.

This is the generic repository class

public class MyRepository
{
    private const string containerName = "myEntities";
    private readonly ObjectContext context;
    private readonly Hashtable objectSets;

    // Track whether Dispose has been called.
    private bool disposed;

    public MyRepository()
    {
        string connectionString = ConfigurationManager.ConnectionStrings[containerName].ConnectionString;

        context = new ObjectContext(connectionString) {DefaultContainerName = containerName};
        context.ContextOptions.LazyLoadingEnabled = true;
        context.ContextOptions.ProxyCreationEnabled = true;

        objectSets = new Hashtable();
    }

    private ObjectSet<TEntity> GetObjectSet<TEntity>() where TEntity : class
    {
        ObjectSet<TEntity> objectSet;

        var type = typeof (TEntity);
        if (objectSets.ContainsKey(type))
        {
            objectSet = objectSets[type] as ObjectSet<TEntity>;
        }
        else
        {
            objectSet = context.CreateObjectSet<TEntity>();
            objectSets.Add(type, objectSet);
        }

        return objectSet;
    }

    public IQueryable<TEntity> GetAll<TEntity>(params string[] entities) where TEntity : class
    {
        ObjectQuery<TEntity> objectQuery = GetObjectSet<TEntity>();

        foreach (var entity in entities)
        {
            objectQuery = objectQuery.Include(entity);
        }

        return objectQuery;
    }


    public void Insert<TEntity>(TEntity entity) where TEntity : class
    {
        ObjectSet<TEntity> objectSet = GetObjectSet<TEntity>();
        objectSet.AddObject(entity);
    }

    public void Update<TEntity>(TEntity entity) where TEntity : class
    {
        ObjectSet<TEntity> objectSet = GetObjectSet<TEntity>();

        EntityKey key = objectSet.Context.CreateEntityKey(objectSet.EntitySet.Name, entity);

        object originalItem;
        if (objectSet.Context.TryGetObjectByKey(key, out originalItem))
        {
            objectSet.ApplyCurrentValues(entity);
        }
        else
        {
            objectSet.Attach(entity);
            objectSet.ApplyCurrentValues(entity);
        }
    }

    public void Delete<TEntity>(TEntity entity) where TEntity : class
    {
        ObjectSet<TEntity> objectSet = GetObjectSet<TEntity>();
        objectSet.DeleteObject(entity);
    }

    public void SaveChanges()
    {
        try
        {
            context.SaveChanges();
        }
        catch (Exception ex)
        {
            ex.ToString();
            throw ex;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        // Take yourself off the Finalization queue 
        // to prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called
        if (!disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        disposed = true;
    }

    ~MyRepository()
    {
        Dispose(false);
    }
}

This is the interface used to expose the methods:

public interface IEntityRepository : IDisposable
{
    void Delete<TEntity>(TEntity entity) where TEntity : class;

    IQueryable<TEntity> GetAll<TEntity>(params string[] entities) where TEntity : class;

    void Insert<TEntity>(TEntity entity) where TEntity : class;

    void Update<TEntity>(TEntity entity) where TEntity : class;

    void SaveChanges();
}

回答1:


The main disadvantage of using a generic repository instead of having a concrete repository implementation per Aggregate root, is that you cannot create specific methods for specific usages.

Having all your repositories inherit from a base repository let you create methods like: GetProductsInsSock() or things like UpdateOnlyProductsThatSatisfySomething().

But some workarounds are available! ;-)

Keep on using your generic repository if it suits to your team, the only thing you might have to add is a method that accept a Specification as parameter. As Eric Evans and Martin Fowler say:

The central idea of Specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against.

In your case it could work as a filter to retrieve the right entities without having the need to create a specific method.

You could simply add this to your IRepository interface:

IEnumerable<T> Find(Specification<T> predicate);

The method implementation in your Repository class would be like this:

public class Repository<T> : IRepository<T> where T : class
{
  public Repository(IDbContext context)
  {
    _context = context;
    _dbset   = context.Set<T>();
  }

  // some code...

  public IEnumerable<T> Find(Specification<T> specification)
  {
    return _dbset.Where(specification.Predicate);
  }

  // some code...
}

The Specification class could look like this:

public class Specification<T>
{
  public Specification(Expression<System.Func<T, bool>> predicate)
  {
    _predicate = predicate;
  }

  internal Expression<System.Func<T, bool>> Predicate
  {
    get { return _predicate; }
  }

  private readonly Expression<System.Func<T, bool>> _predicate;
}

A call example:

var specification = ProductSpecification.InStock();
var product = Repository.Find(specification).FirstOrDefault();

And finally, the ProductSpecification class:

internal static class ActionSpecification
{
  internal static Specification<Product> InStock()
  {
    return new Specification<Product>(p => p.IsInStock == true);
  }
}


来源:https://stackoverflow.com/questions/17224015/entity-framework-4-1-generic-repository-set-up

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!