首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ASP.NET MVC的最佳存储库模式

ASP.NET MVC的最佳存储库模式
EN

Stack Overflow用户
提问于 2012-06-07 11:21:13
回答 4查看 45.2K关注 0票数 64

我最近学习了ASP.NET MVC (我喜欢它)。我正在与一家公司合作,该公司使用依赖注入在每个请求中加载一个repository实例,我熟悉使用该存储库。

但现在我正在编写自己的几个MVC应用程序。我不完全了解我的公司使用存储库的方式和原因,我正在尝试确定实现数据访问的最佳方法。

我使用的是C#和实体框架(所有最新版本)。

我看到了三种处理数据访问的一般方法。

每次我访问数据时,在using语句中使用

  1. Regular DB context。这很简单,而且运行得很好。但是,如果两个位置需要在一个请求中读取相同的数据,则必须读取数据两次。(如果每个请求都有一个存储库,那么两个位置都会使用相同的实例,我知道第二次读取只会返回第一次读取的数据。)
  2. 典型的repository pattern。出于我不理解的原因,这种典型的模式需要为数据库中使用的每个表创建一个包装器类。在我看来这是不对的。实际上,因为它们也是作为接口实现的,所以从技术上讲,我将为每个表创建两个包装类。EF为我创建表格。我不相信这种方法是有意义的。
  3. 还有一个generic repository pattern,其中创建了一个存储库类来为所有实体对象提供服务。这对我来说更有意义。但这对其他人来说有意义吗?上面的链接是最佳方法吗?

我很乐意从其他人那里得到一些关于这个话题的意见。你是在写你自己的存储库,还是使用上面的其中之一,或者做一些完全不同的事情。请分享。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-06-07 12:04:15

我使用了#2和#3的混合,但如果可能的话,我更喜欢严格的通用存储库(甚至比#3的链接中建议的还要严格)。#1并不好,因为它在单元测试中表现不佳。

如果你有一个较小的域,或者需要限制你的域允许查询的实体,我认为#2或#3定义了实体特定的存储库接口,它们本身实现了通用存储库-是有意义的。然而,我发现为我想要查询的每个实体编写一个接口和一个具体的实现是很费力的,也是没有必要的。public interface IFooRepository : IRepository<Foo>有什么好处(再说一次,除非我需要限制开发人员使用一组允许的聚合根)?

我只是使用AddRemoveGetGetDeferredCountFind方法来定义我的通用存储库接口(Find返回一个允许LINQ的IQueryable接口),创建一个具体的通用实现,然后调用它。我在很大程度上依赖于Find,因此也就依赖于LINQ。如果我需要多次使用一个特定的查询,我会使用扩展方法,并使用LINQ编写查询。

这就满足了我95%的持久化需求。如果我需要执行一些一般不能完成的持久化操作,我会使用自己开发的ICommand应用程序接口。例如,假设我正在使用NHibernate,并且我需要作为域的一部分执行一个复杂的查询,或者我可能需要执行一个批量命令。API大致如下所示:

代码语言:javascript
复制
// marker interface, mainly used as a generic constraint
public interface ICommand
{
}

// commands that return no result, or a non-query
public interface ICommandNoResult : ICommand
{
   void Execute();
}

// commands that return a result, either a scalar value or record set
public interface ICommandWithResult<TResult> : ICommand
{
   TResult Execute();
}

// a query command that executes a record set and returns the resulting entities as an enumeration.
public interface IQuery<TEntity> : ICommandWithResult<IEnumerable<TEntity>>
{
    int Count();
}

// used to create commands at runtime, looking up registered commands in an IoC container or service locator
public interface ICommandFactory
{
   TCommand Create<TCommand>() where TCommand : ICommand;
}

现在,我可以创建一个接口来表示特定的命令。

代码语言:javascript
复制
public interface IAccountsWithBalanceQuery : IQuery<AccountWithBalance>
{
    Decimal MinimumBalance { get; set; }
}

我可以创建一个具体的实现,使用原始的SQL、NHibernate HQL等等,并将其注册到我的服务定位器中。

现在,在我的业务逻辑中,我可以这样做:

代码语言:javascript
复制
var query = factory.Create<IAccountsWithBalanceQuery>();
query.MinimumBalance = 100.0;

var overdueAccounts = query.Execute();

您还可以将规范模式与IQuery结合使用来构建有意义的、用户输入驱动的查询,而不是拥有一个具有大量令人困惑的属性的接口,但前提是您不会发现规范模式本身就令人困惑;)。

最后一个难题是,您的存储库何时需要执行特定的pre-和-post存储库操作。现在,您可以非常容易地为特定实体创建通用存储库的实现,然后覆盖相关方法并执行您需要做的事情,并更新IoC或服务定位器注册并使用它。

然而,有时这种逻辑是横切的,并且很难通过覆盖存储库方法来实现。所以我创建了IRepositoryBehavior,它基本上是一个事件接收器。(下面只是我脑海中的一个粗略的定义)

代码语言:javascript
复制
public interface IRepositoryBehavior
{
    void OnAdding(CancellableBehaviorContext context);
    void OnAdd(BehaviorContext context);

    void OnGetting(CancellableBehaviorContext context);
    void OnGet(BehaviorContext context);

    void OnRemoving(CancellableBehaviorContext context);
    void OnRemove(BehaviorContext context);

    void OnFinding(CancellableBehaviorContext context);
    void OnFind(BehaviorContext context);

    bool AppliesToEntityType(Type entityType);
}

现在,这些行为可以是任何东西。审核、安全检查、软删除、执行域约束、验证等。我创建了一个行为,将其注册到IoC或服务定位器中,并修改我的通用存储库以获取已注册的IRepositoryBehavior的集合,根据当前存储库类型检查每个行为,并将操作包装在每个适用行为的前/后处理程序中。

这是一个软删除行为的例子(软删除意味着当有人请求删除一个实体时,我们只是将它标记为已删除,这样它就不会再次被返回,但实际上永远不会被物理删除)。

代码语言:javascript
复制
public SoftDeleteBehavior : IRepositoryBehavior
{
   // omitted

   public bool AppliesToEntityType(Type entityType)
   {
       // check to see if type supports soft deleting
       return true;
   }

   public void OnRemoving(CancellableBehaviorContext context)
   {
        var entity = context.Entity as ISoftDeletable;
        entity.Deleted = true; // when the NHibernate session is flushed, the Deleted column will be updated

        context.Cancel = true; // set this to true to make sure the repository doesn't physically delete the entity.
   }
}

是的,这基本上是NHibernate事件侦听器的简化和抽象实现,但这就是我喜欢它的原因。A)我可以在不引入NHibernate的情况下对行为进行单元测试B)我可以在NHibernate之外使用这些行为(假设存储库是包装REST服务调用的客户端实现) C) NH的事件侦听器可能是一个真正的麻烦;)

票数 35
EN

Stack Overflow用户

发布于 2012-06-07 11:37:31

我推荐1号,但有几点需要注意。第二个似乎是最常见的,但在我的经验中,存储库最终只是一个混乱的查询垃圾场。如果你使用一个通用的存储库(2),它只是一个围绕DBContext的薄薄的包装器,真的有点无意义,除非你计划改变ORM(坏主意)。

但是,当我直接访问DBContext时,我更喜欢使用管道和过滤器模式,这样您就可以重用通用逻辑,例如

代码语言:javascript
复制
items = DBContext.Clients
    .ByPhoneNumber('1234%')
    .ByOrganisation(134);

ByPhoneNumber和By Organisation只是扩展方法。

票数 12
EN

Stack Overflow用户

发布于 2017-07-13 14:15:55

在这里我们来评选Asp.Net MVC中的最佳存储库模式:

Repository模式在应用程序的数据层和域层之间添加了一个分离层。它还使应用程序的数据访问部分具有更好的可测试性。

数据库工厂(IDatabaseFactory.cs):

代码语言:javascript
复制
public interface IDatabaseFactory : IDisposable
{
    Database_DBEntities Get();
}

数据库工厂实现(DatabaseFactory.cs):

代码语言:javascript
复制
public class DatabaseFactory : Disposable, IDatabaseFactory
{
    private Database_DBEntities dataContext;
    public Database_DBEntities Get()
    {
        return dataContext ?? (dataContext = new Database_DBEntities());
    }

    protected override void DisposeCore()
    {
        if (dataContext != null)
            dataContext.Dispose();
    }
}

基本接口(IRepository.cs):

代码语言:javascript
复制
public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Update(T entity);
    void Detach(T entity);
    void Delete(T entity);
    T GetById(long Id);
    T GetById(string Id);
    T Get(Expression<Func<T, bool>> where);
    IEnumerable<T> GetAll();
    IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
    void Commit();
}

抽象类(Repository.cs):

代码语言:javascript
复制
public abstract class Repository<T> : IRepository<T> where T : class
{
    private Database_DBEntities dataContext;
    private readonly IDbSet<T> dbset;
    protected Repository(IDatabaseFactory databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        dbset = DataContext.Set<T>();
    }

    /// <summary>
    /// Property for the databasefactory instance
    /// </summary>
    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    /// <summary>
    /// Property for the datacontext instance
    /// </summary>
    protected Database_DBEntities DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    /// <summary>
    /// For adding entity
    /// </summary>
    /// <param name="entity"></param>
    public virtual void Add(T entity)
    {
        try
        {
            dbset.Add(entity);
            //  dbset.Attach(entity);
            dataContext.Entry(entity).State = EntityState.Added;
            int iresult = dataContext.SaveChanges();
        }
        catch (UpdateException ex)
        {
        }
        catch (DbUpdateException ex) //DbContext
        {
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// For updating entity
    /// </summary>
    /// <param name="entity"></param>
    public virtual void Update(T entity)
    {
        try
        {
            // dbset.Attach(entity);
            dbset.Add(entity);
            dataContext.Entry(entity).State = EntityState.Modified;
            int iresult = dataContext.SaveChanges();
        }
        catch (UpdateException ex)
        {
            throw new ApplicationException(Database_ResourceFile.DuplicateMessage, ex);
        }
        catch (DbUpdateException ex) //DbContext
        {
            throw new ApplicationException(Database_ResourceFile.DuplicateMessage, ex);
        }
        catch (Exception ex) {
            throw ex;
        }
    }

    /// <summary>
    /// for deleting entity with class 
    /// </summary>
    /// <param name="entity"></param>
    public virtual void Delete(T entity)
    {
        dbset.Remove(entity);
        int iresult = dataContext.SaveChanges();
    }

    //To commit save changes
    public void Commit()
    {
        //still needs modification accordingly
        DataContext.SaveChanges();
    }

    /// <summary>
    /// Fetches values as per the int64 id value
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual T GetById(long id)
    {
        return dbset.Find(id);
    }

    /// <summary>
    /// Fetches values as per the string id input
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual T GetById(string id)
    {
        return dbset.Find(id);
    }

    /// <summary>
    /// fetches all the records 
    /// </summary>
    /// <returns></returns>
    public virtual IEnumerable<T> GetAll()
    {
        return dbset.AsNoTracking().ToList();
    }

    /// <summary>
    /// Fetches records as per the predicate condition
    /// </summary>
    /// <param name="where"></param>
    /// <returns></returns>
    public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
    {
        return dbset.Where(where).ToList();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    public void Detach(T entity)
    {
        dataContext.Entry(entity).State = EntityState.Detached;
    }

    /// <summary>
    /// fetches single records as per the predicate condition
    /// </summary>
    /// <param name="where"></param>
    /// <returns></returns>
    public T Get(Expression<Func<T, bool>> where)
    {
        return dbset.Where(where).FirstOrDefault<T>();
    }
}

如何在控制器中访问此存储库模式:

1.你有一个用户模型:

代码语言:javascript
复制
public partial class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

2.现在您必须创建UserModel的存储库类

代码语言:javascript
复制
public class UserRepository : Repository<User>, IUserRepository
{
    private Database_DBEntities dataContext;

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    public UserRepository(IDatabaseFactory databaseFactory)
        : base(databaseFactory)
    {
        DatabaseFactory = databaseFactory;
    }

    protected Database_DBEntities DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    public interface IUserRepository : IRepository<User>
    { 
    }
}

3.现在您必须使用所有CRUD方法创建UserService接口(IUserService.cs):

代码语言:javascript
复制
public interface IUserService
{
    #region User Details 
    List<User> GetAllUsers();
    int SaveUserDetails(User Usermodel);
    int UpdateUserDetails(User Usermodel);
    int DeleteUserDetails(int Id);
    #endregion
}

4.现在您必须使用所有CRUD方法创建UserService接口(UserService.cs):

代码语言:javascript
复制
public class UserService : IUserService
{
    IUserRepository _userRepository;
    public UserService() { }
    public UserService(IUserRepository userRepository)
    {
        this._userRepository = userRepository;
    }

    public List<User> GetAllUsers()
    {
        try
        {
            IEnumerable<User> liUser = _userRepository.GetAll();
            return liUser.ToList();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Saves the User details.
    /// </summary>
    /// <param name="User">The deptmodel.</param>
    /// <returns></returns>
    public int SaveUserDetails(User Usermodel)
    {
        try
        {
            if (Usermodel != null)
            {
                _userRepository.Add(Usermodel);
                return 1;
            }
            else
                return 0;
        }
        catch
        {
            throw;
        }
   }

   /// <summary>
   /// Updates the User details.
   /// </summary>
   /// <param name="User">The deptmodel.</param>
   /// <returns></returns>
   public int UpdateUserDetails(User Usermodel)
   {
       try
       {
           if (Usermodel != null)
           {
               _userRepository.Update(Usermodel);
               return 1;
           }
           else
               return 0;
       }
       catch
       {
           throw;
       }
   }

   /// <summary>
   /// Deletes the User details.
   /// </summary>
   /// <param name="Id">The code identifier.</param>
   /// <returns></returns>
   public int DeleteUserDetails(int Id)
   {
       try
       {
           User Usermodel = _userRepository.GetById(Id);
           if (Usermodel != null)
           {
               _userRepository.Delete(Usermodel);
               return 1;
           }
           else
               return 0;
       }
       catch
       {
           throw;
       }
   }
}

现在,您已经为存储库模式进行了所有设置,并且可以访问User 中的所有数据

代码语言:javascript
复制
//Here is the User Controller 
public class UserProfileController : Controller
{
    IUserService _userservice;
    public CustomerProfileController(IUserService userservice)
    {
        this._userservice = userservice;
    }

    [HttpPost]
    public ActionResult GetAllUsers(int id)
    {
        User objUser=new User();

        objUser = _userservice.GetAllUsers().Where(x => x.Id == id).FirstOrDefault();
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10925257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档