【问题标题】:Should I be using a Generic Repository with Entity Framework 5?我应该使用 Entity Framework 5 的通用存储库吗?
【发布时间】:2013-07-17 20:51:38
【问题描述】:

我目前正在使用具有通用存储库和工作单元模式的实体框架。我的模型类似于this article中描述的模型

我过去曾使用过通用存储库,并且非常喜欢它可以提供的全局功能。但是,在将它与 Entity Framework 一起使用时,我似乎每天都会遇到更多问题。在处理父/子/连接关系时,这些问题似乎更加突出。

使用带有 EF 的通用存储库开始在我的嘴里留下不好的味道,我开始认为使用带有 EF 的通用存储库是错误的方法。

有人可以帮我指引正确的方向吗?

【问题讨论】:

  • 这取决于您拥有通用存储库/UoW 的目标。如果您认为 DbSet<TEntity> 已经是一个通用存储库,并且 DbContext (SaveChanges) 是您的 UoW,那么您可能会复制这些概念。它可能会使测试更容易,但代价是具有另一个抽象级别。我将它用于我的 Repo / UoW,并用于测试我使用effort.codeplex.com
  • 我使用通用存储库的唯一真正目的是拥有全局的通用 CRUD 方法。
  • DbSet<> 已经完成了这些。在通用存储库上进行测试更容易,并且可以解耦对实体框架的依赖,如果这很重要的话。
  • 性能和复杂性也是需要考虑的事情。通过 EF 进行数据访问比 ADO.net 慢得多,并且 EF 为您的应用程序增加了一层额外的复杂性,最终使您对 DAL 的控制更少。
  • @kmdsax 我认为问题是是否使用带有 EF 的存储库,而不是是否使用 EF。

标签: c# entity-framework generics unit-of-work


【解决方案1】:

本文的方法确实会让人感到痛苦,因为您已经在 EF 中拥有了一个通用存储库和一个通用 IUnitOfWork,而为每种类型创建特定存储库只是消除了通用的好处!

我在这里发布了我如何拥有一个通用存储库和我的 IUnitOfWork 的示例,这样您就可以拥有一个非常好的存储库!

public interface IUnitOfWork : IDisposable
{
    void Save();
    void Save(SaveOptions saveOptions);
}

public interface IRepository<TEntity> : IDisposable where TEntity : class
{
    IUnitOfWork Session { get; }
    IList<TEntity> GetAll();
    IList<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate);
    bool Add(TEntity entity);
    bool Delete(TEntity entity);
    bool Update(TEntity entity);
    bool IsValid(TEntity entity);
}

实现类似于:

public class Repository : Component, IRepository
{

    protected DbContext session;

    public virtual IUnitOfWork Session
    {
        get
        {
            if (session == null)
                throw new InvalidOperationException("A session IUnitOfWork do repositório não está instanciada.");
            return (session as IUnitOfWork);
        }
    }

    public virtual DbContext Context
    {
        get
        {
            return session;
        }
    }

    public Repository(IUnitOfWork instance)
    {
        SetSession(instance);
    }

    public IList<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return session.Set<TEntity>().ToList();
    }

    public IList<TEntity> GetAll<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return session.Set<TEntity>().Where(predicate).ToList();
    }

    public bool Add<TEntity>(TEntity entity) where TEntity : class
    {
        if (!IsValid(entity))
            return false;
        try
        {
            session.Set(typeof(TEntity)).Add(entity);
            return session.Entry(entity).GetValidationResult().IsValid;
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
                throw new Exception(ex.InnerException.Message, ex);
            throw new Exception(ex.Message, ex);
        }
    }

    public bool Delete<TEntity>(TEntity entity) where TEntity : class
    {
        if (!IsValid(entity))
            return false;
        try
        {
            session.Set(typeof(TEntity)).Remove(entity);
            return session.Entry(entity).GetValidationResult().IsValid;
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
                throw new Exception(ex.InnerException.Message, ex);
            throw new Exception(ex.Message, ex);
        }
    }

    public bool Update<TEntity>(TEntity entity) where TEntity : class
    {
        if (!IsValid(entity))
            return false;
        try
        {
            session.Set(typeof(TEntity)).Attach(entity);
            session.Entry(entity).State = EntityState.Modified;
            return session.Entry(entity).GetValidationResult().IsValid;
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
                throw new Exception(ex.InnerException.Message, ex);
            throw new Exception(ex.Message, ex);
        }
    }

    public virtual bool IsValid<TEntity>(TEntity value) where TEntity : class
    {
        if (value == null)
            throw new ArgumentNullException("A entidade não pode ser nula.");
        return true;
    }

    public void SetSession(IUnitOfWork session)
    {
        SetUnitOfWork(session);
    }

    protected internal void SetUnitOfWork(IUnitOfWork session)
    {
        if (!(session is DbContext))
            throw new ArgumentException("A instância IUnitOfWork deve um DbContext.");
        SetDbContext(session as DbContext);
    }

    protected internal void SetDbContext(DbContext session)
    {
        if (session == null)
            throw new ArgumentNullException("DbContext: instance");
        if (!(session is IUnitOfWork))
            throw new ArgumentException("A instância DbContext deve implementar a interface IUnitOfWork.");
        this.session = session;
    }

}

【讨论】:

  • 在添加try catch的意义何在?另外,为什么不 session.Set().Add(entity)?
  • 问题是关于一般用途,所以请记住,这只是帮助解释的一个示例。这不是问题的重点。 try catch 的重点是能够显示引发的内部异常,而不是更通用的错误。在这种情况下也可以使用 Set().Add(entity) ,但是在某些情况下我们不能使用通用版本(例如,当您具有继承并且必须使用 .TypeOf 时)
  • 如果你打算重新抛出异常,那么你可以说throw;,因为抛出一个新的异常会丢弃当前的调用堆栈。见stackoverflow.com/questions/881473/…。我也不同意重新抛出内部异常,因为调用该方法的任何人也可以轻松检查内部异常,但是使用此代码,您没有给他们选择。
  • 你没有抓住重点。有一个异常框架实现了这个框架,它将异常转换为使用友好的消息,这就是我们在这里抛出内部异常的原因。请记住,我们正在为 EF 制作一个包装器,因此它可以在这种情况下工作,包装器负责处理异常和抛出。这在其他情况下不好,但在这种情况下这是正确的。注意上下文,这里的 throw 只是更改主消息,整个 excpetion 都在那里(没有信息丢失),只是处理 Excpetion 的 MESSAGE 的问题。
  • 除非您可以实际处理异常,或者您可以在自己的自定义异常中以更有意义的方式对其进行框架化,否则您应该让异常消失,让接口的用户如何处理异常被抛出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
相关资源
最近更新 更多