我的情况是我搞砸了。大约1。5年前,当我采用这个位置而不是重新发明轮子时,我继承了我的代码库,尽管我现在已经知道我应该拥有,但我保持DAL的结构与之前的开发人员完全相同。
基本上有一个文件(现在是15k行代码),用作一堆使用DataSets和TableAdapter来检索数据的DAO。我的xsd文件已经发展到这样的大小,每次打开时它们都会导致R#崩溃视觉工作室,而现在15k行的中间类也需要永远让R#进行分析。更不用说它是丑陋的,它可以工作,但不是很好,并且是调试的绝对噩梦。
到目前为止我尝试过的是切换到NHibernate。 NHibernate是一个很棒的库,但不幸的是它不适应我的应用程序,从主要的开发人员说(Fabio Maulo)它几乎是我的应用程序要求和NHibernate在使用身份作为数据库时的限制的组合PK策略。
所以现在我回到基本上设计我自己的DAL。我正在考虑一些不同的模式,但是想获得你的DAL设计策略。有很多方法和理由以特定的方式实施DAL,所以如果你能解释你的策略以及为什么它最适合你,我将非常感激。
提前致谢!
编辑:让我解释为什么NHibernate不起作用,因为这似乎是立即的反应。我的用户创建了一个“作业”,它实际上只是我的Job类的瞬态表示。在这项工作中,他们将给出一个或一个在创建时也是短暂的权重因子列表。最后,他们提供了一个工作细节列表,这些细节具有与之相关的特定权重因子。因为,在DB中,当我继续工作时权重因子是唯一的,并且当它找到重复的权重因子时,它会降低到权重因子。我尝试在将权重因子分配给详细信息之前运行检查(我不想这样做因为我不想对数据库进行额外调用)但是在NH中调用CreateCriteria也会导致会话中的刷新,根据Fabio,它会破坏我的缓存,从而杀死整个作业的内存表示。 NH邮件列表上的人说我应该切换到GUID,但这不是一个可行的选择,因为转换过程将是一场噩梦。
答案 0 :(得分:1)
对我而言,最合适的是一个非常简单的概念 - 使用DAO类定义并使用反射创建填充和保存它们所需的所有SQL。这种方式没有映射文件,只有简单的类。我的DAO需要一个Entity基类,因此它不是POCO,但这并不困扰我。它支持任何类型的主键,无论是单一标识列还是多列。
答案 1 :(得分:1)
我对NHibernate的体验是,虽然它具有丰富的功能和非常高的性能,但您最终需要成为NHibernate专家才能修复一些意想不到的行为。阅读pro-NHibernate的答案并看到
嗯,也许他用的是长跑 会议(每个业务会话 交易模型),并在这样的 方法,使用身份是 气馁,因为它打破了你的 unitofwork(需要直接冲洗 插入新实体后)。一个 解决方案可能是放弃 身份,并使用HiLo身份 发生器。
完全说明了我的意思。
我所做的是创建一个基于ActiveRecord模式建模的基类,我继承并使用将其附加到存储过程的属性标记继承类,每个存储过程用于Select,Insert,Update和Delete。基类使用Reflection来读取属性并将类的属性值分配给SP参数,在Select()的情况下,将结果SQLDataReader的列值分配给泛型列表的属性。
这就是DataObjectBase的样子:
interface IDataObjectBase<T>
{
void Delete();
void Insert();
System.Collections.Generic.List<T> Select();
void Update();
}
这是从中派生的数据类的示例:
[StoredProcedure("usp_refund_CustRefundDetailInsert", OperationType.Insert)]
[StoredProcedure("usp_refund_CustRefundDetailSelect", OperationType.Select)]
[StoredProcedure("usp_refund_CustRefundDetailUpdate", OperationType.Update)]
public class RefundDetail : DataObjectBase<RefundDetail>
{
[StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Output)]
[StoredProcedureParameter(null, OperationType.Select, ParameterDirection.Input)]
[ResultColumn(null)]
public int? RefundDetailId
{ get; set; }
[StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Select, ParameterDirection.Input)]
[ResultColumn(null)]
public int? RefundId
{ get; set; }
[StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)]
[ResultColumn(null)]
public int RefundTypeId
{ get; set; }
[StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)]
[ResultColumn(null)]
public decimal? RefundAmount
{ get; set; }
[StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)]
[StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)]
[ResultColumn(null)]
public string ARTranId
{ get; set; }
}
我知道我似乎正在重新发明轮子,但我发现所有的库都过于依赖其他库(例如,ActiveRecord + NHibernate,这是紧随其后的第二个)或者太复杂而无法使用和管理。
我制作的库非常轻量级(可能是几百行C#)并且除了为参数赋值和执行SP之外什么也做不了。它也非常适合代码生成,因此最终我希望不编写任何数据访问代码。我也喜欢它使用类实例而不是静态类,这样我就可以将数据传递给查询而不需要一些笨拙的标准集合或HQL。选择()意味着“变得更像我”。
答案 2 :(得分:0)
如果您的DAL被写入接口,那么切换到NHibernate或者可以比较的东西会更容易(我更喜欢Fluent-NHibernate,但我离题了)。那么为什么不花时间而不是重构DAL来使用接口,然后用NH或你选择的ORM编写一个新的实现呢?
答案 3 :(得分:0)
在最近的项目中,我们已停止编写单独的DAL。
相反,我们使用对象关系映射器(在我们的案例中为Entity Framework)。然后我们直接针对ORM使用业务层程序。
在某些情况下,这为我们节省了90%以上的开发工作。
答案 4 :(得分:0)
我的第一步是打破15 KLOC怪物的代码,然后提出创建新DAL的策略。
答案 5 :(得分:0)
如果您使用SQL Server,Linq to SQL很不错。有一个LinqToSQL提供商可以访问Access和MySQL。我没有测试过它。 LinqToSql遵循UnitOfWork模型,它类似于ADO.NET的运行方式。您对数据的本地副本进行一系列更改,然后通过一次更新调用提交所有更改。我觉得这很干净。
您还可以自己扩展DataRow类,以提供对字段的强类型访问。我使用XSLT根据每个表的元数据生成DataRow后代。我有一个通用的DataTable后代。 MyDataTable,其中T是我的派生行。我知道MS的强类型数据集做了类似的事情,但我想要一个我完全控制的轻量级通用版本。完成此操作后,您可以编写查询数据库并填充DataTable的静态访问方法。
您将负责将DataTable中的更改写回DataSource。我会写一个创建更新,插入和删除的泛型类或方法。
祝你好运!
答案 6 :(得分:0)
我使用我的包装器来获取SP,以便在性能不是目标时实现最快的数据检索和L2S。我的DAL使用存储库模式和封装逻辑进行TDD。