ASP.NET MVC软件设计模式:DI,存储库,服务层

Tim*_*ess 6 asp.net-mvc

编辑

我应该将服务层和存储库层放入一个项目中,以便Web项目能够引用DbContext对象吗?现在我的web(控制器)无法引用dbcontext对象.什么是正确的方法?

// service and repository are together
(View <- Controller) -> (Service -> Repository -> EF DbContext) -> (DB) 
// separate service and repository layer
(View <- Controller) -> (Service) -> (Repository -> EF DbContext) -> (DB)
Run Code Online (Sandbox Code Playgroud)

以下是原始问题

我知道SO是一个很好的社区,可以发布关于mvc设计模式的问题.请给我你的建议,我将非常感谢你的帮助.谢谢!

我们正在计划一个新项目,我们的首要任务是开发一个可扩展且松散耦合的应用程序.

我是软件开发的新手; 我做了一些关于MVC音乐商店教程的阅读,然后是Steven Sanderson(Apress)的一本名为Pro ASP.NET MVC 3 Framework的书.从书中我学到了DDD(域驱动设计)和其他一些概念,如存储库和依赖注入.我按照这本书建立了SportsStore网站,并对DI有了一些基本的了解.但我个人认为这个例子没有将业务逻辑层分开,所以我对此进行了研究,我发现了一种称为服务层模式的模式,根据我的理解,它将业务逻辑层分开.基于此,我为我的新项目(下面的示例项目)提出了一个结构.

我需要实现IDisposable接口吗?如果是的话,在哪里和为什么?这种结构对于一个规模较大的项目是否可行?

样本数据库设计:产品(一)----(很多)ProductCategoryRs(很多)----(一)类

该解决方案包含3个项目:存储库,服务,Web

库:

定义IRepository接口,基本的CRUD操作

这些签名是否足够?我应该添加TEntity GetById(对象ID);

public interface IRepository<TEntity>
{
    IQueryable<TEntity> All { get; }
    void Create(TEntity item);
    void Update(TEntity item);
    void Delete(TEntity item);
    void SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

实现通用的Repository类

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    STOREEntities context;
    public Repository()
    {
        context = new STOREEntities();
    }
    public IQueryable<TEntity> All
    {
        get
        {
            return context.Set<TEntity>();
        }
    }
    public void Create(TEntity item)
    {
        context.Set<TEntity>().Add(item);
    }
    public void Update(TEntity item)
    {
        context.Entry<TEntity>(item).State = System.Data.EntityState.Modified;
    }
    public void Delete(TEntity item)
    {
        context.Set<TEntity>().Remove(item);
    }
    public void SaveChanges()
    {
        context.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

服务:定义IProductService接口,在此扩展业务逻辑.

public interface IProductService
{
    IEnumerable<Product> Products { get; }
    IEnumerable<Product> Get(Expression<Func<Product, Boolean>> filter);
    Product GetByProductId(int productId);
    void AddProduct(Product product);
    void EditProduct(Product product);
    void RemoveProduct(Product product);
    void SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

实施产品服务

    public class ProductService : IProductService
{
    IRepository<Product> repository; //Inject
    public ProductService(IRepository<Product> repo)
    {
        repository = repo;
    }
    public IEnumerable<Product> Products
    {
        get { return repository.All; }
    }
    public IEnumerable<Product> Get(Expression<Func<Product, bool>> filter)
    {
        return repository.All.Where(filter);
    }
    public Product GetByProductId(int productId)
    {
        return repository.All.SingleOrDefault(p => p.ProductID == productId);
    }
    public void AddProduct(Product product)
    {
        repository.Create(product);
    }
    public void EditProduct(Product product)
    {
        repository.Update(product);
    }
    public void RemoveProduct(Product product)
    {
        repository.Delete(product);
    }
    public void SaveChanges()
    {
        repository.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

Web项目,从服务检索数据并转换为viewmodel和显示.ProductController代码

public class ProductController : Controller
{
    IProductService productService; //inject
    public ProductController(IProductService service)
    {
        productService = service;
    }
    public ActionResult Index()
    {
        var products = productService.Products; //retrieve from service layer
        return View(products);
    }
}
Run Code Online (Sandbox Code Playgroud)

rse*_*nna 2

我相信你真的应该TEntity GetById(int id)在你的通用界面中添加一个IRepository<TEntity>

为什么?因为如果您不这样做,并且想要在业务层上获取单个记录,那么您只有两个选择(在存储库、数据访问层上):

  1. 返回一个完整的、“非惰性”集合,这意味着您将返回 100,000 条记录,以便使用单个记录。
  2. 返回像这样的惰性集合IQueryable<TEntity>,是的,这将允许您从数据库中获取单个记录,但可能会导致很多令人讨厌的副作用

第一个选项显然是错误的。第二个是有争议的,但是(除非你的项目只有你作为一个开发人员,并且你真的真的知道你在做什么)它可能存在泄漏和不安全。因此,如果您确实需要一条记录(有时您肯定会这样做),请公开一个完全可以做到这一点的方法。

话虽如此,出于与上述完全相同的原因,您也不应该公开。IQueryable<TEntity> All { get; }相反,通过调用IEnumerable<TEntity> All { get; },使您的具体通用存储库类返回一个真实的集合context.Set<TEntity>().ToList()

编辑

关于 IDisposable:

我能想到的实现 IDisposable 接口只有两个(相关的)原因:

  1. 处置非托管资源
  2. 实现 RAII 模式的一种很酷的方法。

就您而言,您可能应该在存储库实现中使用它。请查看这个 SO 问题以获取更多信息。