将我的 DAL 代码重构为领域驱动设计或更现代的设计 (C# 3.5)?
Posted
技术标签:
【中文标题】将我的 DAL 代码重构为领域驱动设计或更现代的设计 (C# 3.5)?【英文标题】:Re-factor my DAL code to a Domain Driven Design or more modern design (C# 3.5)? 【发布时间】:2014-07-11 21:48:43 【问题描述】:开发仅限于 Visual Studio 2010(客户认可的软件)。我们需要通过存储过程来访问数据。我想避免过于复杂的日程安排。我看到的大多数设计都涉及 EF 和 LINQ,不知道如何为 procs 设计?
我想创建一个单独的代码库项目(使用 Web UI):
Application.Domain
- Interact get/put stored procedures, entities
Application.Web
- containing Web UI (JQuery, AJAX), WCF Service
谁能给我关于如何处理 Application.Domain 的示例代码?
示例,我已阅读:
http://www.developer.com/net/dependency-injection-best-practices-in-an-n-tier-modular-application.html
http://www.kenneth-truyers.net/2013/05/12/the-n-layer-myth-and-basic-dependency-injection/
DAL\AppDAL.cs:
public static IEnumerable<TasCriteria> GetTasCriterias()
using (var conn = new SqlConnection(_connectionString))
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = "IVOOARINVENTORY_GET_TASCRITERIA";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
var types = (from c in dataset.Tables[0].AsEnumerable()
select new TasCriteria()
TasCriteriaId = Convert.ToInt32(c["TasCriteriaId"]),
TasCriteriaDesc= c["CriteriaDesc"].ToString()
).ToList<TasCriteria>();
return types;
模型\TasCriteria.cs:
public class TasCriteria
public int TasCriteriaId get; set;
public string TasCriteriaDesc get; set;
服务\Service.svc:
[OperationContract]
[WebInvoke(ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest, Method = "GET")]
public List<TasCriteria> GetTasCriteriaLookup()
var tc = InventoryDAL.GetTasCriterias();
return tc.ToList();
【问题讨论】:
为什么一定要使用存储过程?注意:您可以在使用存储过程时使用 EF(和 L2S)。如果这样做,最好将它们限制为简单的 CRUD 操作,这样您的业务逻辑就不会泄漏到 DB 层。但我想知道为什么你必须首先使用存储过程。 是的,EF 可以使用 procs。我们有一个积极的时间表,并有可用的过程。我们觉得使用它们而不是重写 LINQ 会更快。应用程序的很大一部分是报告,所有逻辑都在 procs 中......重写它们会很耗时。 你说你不确定如何为存储过程设计这个。你的设计不会改变。看起来你的层布置得很好。具体来说,你在哪里卡住了?如果你使用 EF,你应该能够将你的 sproc 拖到设计器上并开始使用它。 我想我觉得我的设计太简单了。为了了解您的观点,我认为引擎盖下的 EF 就是 ADO.NET。使用 EF 进行额外的抽象层是否会导致 procs 的开销(可以认为是 db 之上的层抽象)? 如果我拥有所有 procs,拥有 EF 的优势在哪里? 【参考方案1】:如果你:
时间紧迫 通过 sprocs/views 已将大部分业务逻辑放在 DB 端 之前没有与 EF 合作过我建议你看看Microsoft Enterprise Library,尤其是数据应用程序块。它将简化您的 所有 DAL 功能(不使用任何 ORM 框架),并在 Unity 的帮助下遵循依赖倒置原则,Unity 是来自 Microsoft 的依赖注入容器。
一些有用的数据应用程序块概念:
输出映射器
输出映射器获取从数据库返回的结果集(在 行和列的形式)并将数据转换为一个序列 对象。
// Create and execute a sproc accessor that uses default parameter and output mappings
var results = db.ExecuteSprocAccessor<Customer>("CustomerList", 2009, "WA");
阅读整个Retrieving Data as Objects 主题。
参数映射器
参数映射器获取您要传递给 查询并将每一个转换为一个 DbParameter 对象。
// Use a custom parameter mapper and the default output mappings
IParameterMapper paramMapper = new YourCustomParameterMapper();
var results = db.ExecuteSprocAccessor<Customer>("Customer List", paramMapper, yourCustomParamsArray);
对于实体生成,我会尝试使用这个tool。它从存储过程返回的结果集中构建一个 POCO 类。我还没有尝试过这个工具,也许有更好的选择,但它可以让你开始,所以你不必手动做。
如果您使用的是 .NET 3.5,那么您必须使用 Enterprise Library 5.0。
我希望这会引导你朝着正确的方向前进。
【讨论】:
谢谢你的建议,我差点忘了企业库。 一个请求,对 paramMapper 和 CustomParamsArray 有点困惑......你能给我举个例子吗?例如,我的过程“Tracking_Get_Requests”中有一个参数“@requestId int”,它是如何工作的? var results = db.ExecuteSprocAccessor首先,确保您使用依赖注入(例如 ninject 或 unity(或许多其他免费提供的))抽象 DAL。很可能让您的 DAL 松散耦合,因此如果您稍后决定 EF(或任何其他 ORM)不是最好的课程,那么更改它不会花费血...
您不希望有一个带有静态方法的 AppDAL 类来调用 SP。如果只是为了单元测试,至少添加一个接口并使用注入。
无论您将使用 EF 还是 Nhibernate 或任何其他 ORM,该决定都应该封装在您的 DAL 中,而不是泄漏到其他层中。域层应该使用来自 DAL 的存储库类的接口(并且那些包含对所选 ORM 或数据访问类的引用)。
这些存储库将调用存储过程并返回您的模型类 (POCO)。
在我最近的一个项目中,我们有这个接口来提供基本的 CRUD 操作:
public interface IRepository<T> where T : DomainEntity
T Get(Int64 id);
void SaveOrUpdate(T entity);
void Delete(T entity);
IQueryable<T> Find();
DomainEntity 是一个非常简单的类,所有模型类都继承。 在我们需要使用存储过程的极少数情况下,我会创建一个额外的接口来提供 GetXXXEntity 方法(1 个或多个),它会执行对 SP 的实际调用。
所以,当我需要使用它的 ID 从数据库中获取实体时,它看起来像:
_diWrapper.GetRepository<Person>().Get(id);
_diWrapper.GetRepository<Order>().Get(id);
_diWrapper 是我的依赖注入容器的包装器(在本例中为 ninject)。我使用了一个包装器,因此如果需要,我可以轻松地将 ninject 替换为其他东西。
在我需要使用 linq 的常见情况下:
_diWrapper.GetRepository<Person>().Find().Where(q => q.Name == "Jack").ToList();
重要的是我可以相当快地用其他任何东西替换 Nhibernate。
我强烈建议您查看 Fluent NHibernate,因为它提供了一个不需要太多编码的简单解决方案。
编辑:这是实现 IRepository 接口的存储库类的示例:
public class NhibernateRepository<T> : IRepository<T> where T : DomainEntity, new()
private ISession _session;
public NhibernateRepository()
_session = BaseNHibernateHelper<NHibernateHelper>.GetCurrentSession();
public T Get(Int64 id)
return _session.Get<T>(id);
public void SaveOrUpdate(T entity)
_session.SaveOrUpdate(entity);
public void Delete(T entity)
_session.Delete(entity);
public IQueryable<T> Find()
return _session.Query<T>();
请注意,在构造函数中,我使用了另一个我创建的用于包装会话工厂的休眠助手。这就是我依赖 nhibernate 的地方。
如果我想用另一个 ORM 替换 NH,我只需要修改存储库类(以及底层的支持类),你可以看到 NH 不会泄漏到存储库类之外,并且没有人使用它了解 NH 的用法。
【讨论】:
我真的很喜欢你的方法......如果我以后想更改 ORM,它的灵活代码......快速请求,你能提供一个实现 IRepository 的示例吗?例如:PersonRepository、OrderRepository? 抽象 ORM 会阻止您使用 100% 的强大功能。有很多来自经验丰富的开发人员的资源/文章说,在您的应用程序和 ORM 之间添加额外的抽象层是一个坏主意Say No to the Repository Pattern in your DAL 和 NOT using repository pattern, use the ORM as is (EF) 抱歉询问细节: T : DomainEntity,DomainEntity 来自哪里?得到缺少的指令错误? DomainEntity 是您的基本实体抽象类/接口。通常它只有一个属性,如“int ID get;private set”,所有实体都必须继承自它。 正是@JernejGorički 所写的,只是我的 Id 属性也有一个公共集,因为 NH 设置了它。除此之外没有什么...【参考方案3】:我注意到大多数人都在谈论实施/技术,但没有人提到领域驱动设计的应用或主旨。好吧,DDD 不一定是你可以通过添加 dapper/ef/enterprise 库块来实现的。这些可以提供帮助,SOLID 和 cqs 命令/查询分离之类的东西也可以提供帮助,但这些仅仅是促成因素,还有更多的考虑和问题需要提出。看看 infoq 上的“领域驱动设计快速”以获得更多想法。
【讨论】:
以上是关于将我的 DAL 代码重构为领域驱动设计或更现代的设计 (C# 3.5)?的主要内容,如果未能解决你的问题,请参考以下文章