ASP.NET MVC软件设计模式:依赖注入、仓储、服务层

6

编辑

我应该将服务层和仓储层放在一个项目中,以便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)

以下是原始问题

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

我们正在计划一个新项目,我们的优先事项是开发一个可扩展和松耦合的应用程序。

我是软件开发的新手。我读了MVC音乐商店教程,并跟随Steven Sanderson(Apress)的Pro ASP.NET MVC 3 Framework一书学习了DDD(领域驱动设计)以及一些其他概念,如存储库和依赖注入。我按照这本书来构建了SportsStore网站,并对DI有了一些基本的理解。但我个人认为示例没有分离业务逻辑层,所以我进行了一些研究,找到了一种称为服务层模式的模式,从我的理解来看,它分离了业务逻辑层。基于此,我为我的新项目设计了一个结构(下面是样品项目)。

我需要实现IDisposable接口吗?如果是的话,应该在哪里以及为什么? 这个结构对于一个相对大规模的项目来说可行吗?
样例数据库设计:Product(一)----(多)ProductCategoryRs(多)----(一)Category
该解决方案包含3个项目:Repository、Service、Web
Repository:
定义IRepository接口,基本的CRUD操作
这些签名足够了吗?我应该添加TEntity GetById(object id);吗?
public interface IRepository<TEntity>
{
    IQueryable<TEntity> All { get; }
    void Create(TEntity item);
    void Update(TEntity item);
    void Delete(TEntity item);
    void SaveChanges();
}

实现通用存储库类

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();
    }
}

服务: 定义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();
}

实现产品服务

    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();
    }
}

从服务中检索数据并将其转换为ViewModel以进行显示的Web项目。 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);
    }
}
1个回答

2

我认为你应该在你的IRepository<TEntity>泛型接口中添加一个TEntity GetById(int id)方法。

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

  1. 返回完整的、“非延迟”的集合,这意味着你将返回100,000条记录以使用一条记录。
  2. 返回像IQueryable<TEntity>这样的延迟加载集合,它确实允许你从数据库中获取单个记录,但可能会引起很多令人不快的副作用

第一种选择显然是错误的。第二种选择有争议,但(除非你的项目只有你一个开发人员,并且你真的真的非常了解你在做什么),它可能会泄漏和不安全。因此,如果你确实需要单个记录(有时你肯定会需要),就暴露一个执行这个操作的方法。

说了这些,你也不应该暴露 IQueryable<TEntity> All { get; },原因与上述相同。改用 IEnumerable<TEntity> All { get; },并让你的具体泛型仓储类返回一个真正的集合,例如通过调用 context.Set<TEntity>().ToList()
编辑
关于 IDisposable:
我只能想到两个(相关的)实现 IDisposable 接口的原因:
1. 释放非托管资源 2. 实现 RAII 模式的一种酷炫方式。
在你的情况下,你可能应该在你的仓储实现中使用它。请参考 this SO question 获取更多信息。

-1,尽管您对GetById有很好的理解,但它并不是任何问题的答案。 - Levitikon
1
@Levitikon:他问了关于 IDisposable 的问题,我回答了。这就是一个答案(至少这是我学会的计数方法,不确定你是否一样 :P)。 - rsenna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接