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

2024-01-21

EDIT

我是否应该将服务层和存储库层放入一个项目中,以便 Web 项目能够引用 DbContext 对象?现在我的网络(控制器)无法引用 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 音乐商店教程 http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-1,然后是一本书叫Pro ASP.NET MVC 3 框架,作者:Steven Sanderson (Apress) https://rads.stackoverflow.com/amzn/click/com/1430234040,从书中,我了解了DDD(领域驱动设计)以及其他一些概念,例如存储库和依赖注入。我按照这本书搭建了SportsStore网站,对DI有了一些基本的了解。但我个人认为这个例子没有分离业务逻辑层,所以我对此进行了研究,我发现了一种称为服务层模式的模式,根据我的理解,它分离了业务逻辑层。基于此,我为我的新项目制定了一个结构(下面的示例项目)。

我需要实施吗I一次性界面?如果是,在哪里以及为什么? 这种结构对于规模较大的项目是否可行?

数据库设计示例:产品(一个)----(多个)产品类别Rs(多个)----(一个)类别

该解决方案包含3个项目:Repository、Service、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();
}

实现通用存储库类

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

Web项目,从服务中检索数据并转换为视图模型并显示。 产品控制器代码

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

我相信你真的应该 add a TEntity GetById(int id)给你的IRepository<TEntity>通用接口。

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

  1. 返回一个完整的、“非惰性”集合,这意味着您将返回 100,000 条记录,以便使用单个记录。
  2. 返回一个惰性集合,例如IQueryable<TEntity>,是的,这将允许您从数据库中获取单个记录,但可能会导致很多令人讨厌的副作用 http://www.weirdlover.com/2010/05/11/iqueryable-can-kill-your-dog-steal-your-wife-kill-your-will-to-live-etc/.

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

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

Edit

关于 IDisposable:

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

  1. 处置非托管资源
  2. 一个很酷的方式实施 RAII 模式 http://www.peterprovost.org/blog/post/RAII-and-IDisposable-and-Thoughts-About-OO.aspx.

就你而言,你可能should在您的存储库实现中使用它。请看一下这个问题 https://stackoverflow.com/questions/2975760/should-linq-to-sql-repository-implement-idisposable了解更多信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ASP.NET MVC 软件设计模式:DI、存储库、服务层 的相关文章

随机推荐

  • 节点 - fs.statbirthtimeMs 和 mtimeMs

    我在 Windows 的文件系统 NTFS 上创建了一个空文件 我打了电话fs stat并意识到birthtimeMs和mtimeMs是相等的 这对我的情况很有好处 因为我需要检测是否有人在此期间没有以某种方式更改文件 所以在 Window
  • 找到可执行文件的路径

    我使用 Go 为各种平台编译一个程序 并通过调用相对路径或仅通过其名称 如果它位于 PATH 变量中 来运行它 是否可以找出可执行文件在哪里 比如说 我的程序叫做 foo exe 我可以跑 foo foo 如果它在路径中 subdir su
  • 强制转换 (int?)null 与 new int?() - 哪个更好?

    I use Nullable
  • 没有默认构造函数的值初始化

    我想知道在什么情况下一个类可能没有默认构造函数但仍然会被值初始化 无默认构造函数 的典型情况是存在参数化构造函数但不存在默认构造函数违约 default 引用 2020 年标准第 9 4 1 章 初始化器 常规 最新草案包含等效措辞 对 T
  • Android:正弦波生成

    我正在尝试使用 AudioTrack 生成正弦波 方波和锯齿波 然而 它创建的音频听起来不像纯正弦波 而是像叠加了某种其他波 在使用第一个示例中的方法的同时 如何获得像第二个代码示例中那样的纯正弦波 由于上面的例子只涉及第二个例子中使用的一
  • Git hooks - 如何仅处理属于存储库内特定文件夹的文件?

    我对 git 有点陌生 对 bash 不太擅长 我有一个存储库 如果重要的话 在 Bitbucket 中 它只包含两个文件夹 我已经有一个钩子 用 bash 编写 可以触发 Jenkins 作业 现在我想更改它 因此它将仅针对提交到其中一个
  • 从另一个包加载 spritekit 场景?

    我正在使用 swift 4 2 制作 SpriteKit 框架 并希望包含一些用于场景和动作的 sks 文件 我尝试使用以下代码从包中加载场景 class func newGameScene gt GameScene guard let g
  • 设置文本时停止调整 UITextField 大小

    我有一个视图设置 其中布局了 4 个 UITextFields 并设置了约束 以便它在所有设备上看起来都很棒并且按预期工作 我有一个问题 如果我使用 TextField text some long text 设置文本或 TextField
  • 如何从 sqlite (3.6.21) 表中删除约束?

    我有下表 CREATE TABLE child id INTEGER PRIMARY KEY parent id INTEGER CONSTRAINT parent id REFERENCES parent id description T
  • 跨隐藏跨度搜索文本 (Ctrl+F)

    Is it possible to search for text using your browser s Ctrl F function across three span tags if the middle tag is set t
  • 突出显示特定日期(来自 bean 的值)

    使用素面
  • C++11 中的纯函数

    C 11 中的一个可以以某种方式gcc将函数 不是类方法 标记为const告诉它是pure并且不使用全局内存而仅使用其参数 我试过了gcc s attribute const 它是恰恰我想要的是 但当函数中触及全局内存时 它不会产生任何编译
  • PHP $_POST 不带重音符号

    这个问题已经解决了 看我的回答看看解决方案 我试图通过使用 POST 提交到 PHP 页面的 html 表单向我的数据库添加带有重音字母的文本 问题是重音字母被转换为不可读的字符 我有这个表格
  • 在目录树中查找具有给定扩展名的文件

    我有一个文件夹 其中有一些文件和子文件夹 例如 我需要返回具有给定扩展名的文件名或相对地址 如果文件位于子文件夹中 html 例如 这是给定文件夹的结构 文本 某事 ncx 文本 123 html 内容 opf toc ncx Flow 0
  • Ctrl+。 (点) 使“e”出现,而不是在 Gnome 上的 VSCode 中显示建议

    我在 Gnome 上使用 VSCode 在 Arch 上的 XOrg 上 最近以来 捷径Ctrl 停止工作 尽管仍然列在键盘快捷键中并且所有扩展都被禁用 相反 带有下划线的e出现 当按两次 Enter 或按一次 ESC 时 该字符消失 当写
  • MySQL Join on LIKE 语句

    我需要计算数据库中每个组中有多少用户 不幸的是 数据库设计不是很好 用户 uid 是针对组表中的组存储在 LONGTEXT 字段列名称 owncloudusers 中 owncloudusers 数据示例 i 0 s 36 25C967BD
  • 使用不同的 dockerfiles 从源目录构建 docker 镜像

    我的目标是从相同的源代码构建几个不同的 docker 镜像 我有我的 src 文件夹 node js 项目 在 src 内我有第一个 dockerfile 和第二个 dockerfile 为了构建项目 我使用这个标准命令行 sudo doc
  • 如何在 Rails 7 引擎中使用 Tailwind CSS gem?

    如何在轨道发动机中使用顺风 根据文档 向 Rails 生成器提供 css 参数应该可以工作 使用 Rails 7 0 2 2 引擎生成 rails plugin new tailtest mountable full d postgresq
  • Android PhoneGap 通知状态栏不显示任何内容

    我正在尝试使用名为的 Phonegap 插件在 Android 模拟器设备上显示通知消息StatusBarNotification LINK https github com phonegap phonegap plugins tree m
  • ASP.NET MVC 软件设计模式:DI、存储库、服务层

    EDIT 我是否应该将服务层和存储库层放入一个项目中 以便 Web 项目能够引用 DbContext 对象 现在我的网络 控制器 无法引用 dbcontext 对象 什么是正确的方法 service and repository are t