数据的访问方法有如下方法:
• ADO.NET Connection, Command, DataReader objects
• LINQ to SQL
• ADO.NET Entity Framework
• Remote XML services
• XML data files (or other text files)
大家都知道CSLA的数据访问门户要求提供DataPortal_XYZ方法。
数据访问可以直接把代码放到业务对象类中、分离在单独的类中、分离在单独的程序集中、使用对象工厂方法。
1直接把代码放到业务对象类中:
这种方法直接把代码写在业务对象类中,偶合性很强。
private void DataPortal_Fetch(SingleCriteria<CustomerEdit, int> criteria)
{
using (var ctx = ConnectionManager<SqlConnection>.GetManager("MyDb"))
{
using (var cm = ctx.Connection.CreateCommand())
{
cm.CommandType = CommandTypes.Text;
cm.CommandText = "SELECT Id, Name FROM Customer WHERE id=@id";
using (var dr = new SafeDataReader(cm.ExecuteReader()))
{
dr.Read();
LoadProperty(IdProperty, dr.GetInt32("Id"));
LoadProperty(NameProperty, dr.GetString("Name"));
}
}
}
}
这样的代码清晰直观,性能也是最好的,因为直接使用.net底层的DataReader。同时也可以使用.NET的最新数据访问技术,LINQ to SQL, the ADO.NET Entity Framework。
同时这样的实现是不能达到数据访问的重用,因为代码直接写死在业务类中,没办重用。这种方式就没有数据访问层,只有UI层和业务对象层。
2分离在单独的类中:
分离在单独的类中,相对增加了系统的复杂性,同时也带来其它好处理。分离主要考虑的因素有:
l 可以重用数据库访问层
l 方便从一种数据访问方式转换到另一种访问方式(from LINQ to SQL to the ADO.NET Entity Framework)
l 方便从一种数据库向另一种数据库的转换(From Oracle to SQL Server)
private void DataPortal_Fetch(SingleCriteria<CustomerEdit, int> criteria)
{
using (var dal = new CustomerDal())
{
using (SafeDataReader dr = dal.GetCustomer(criteria.Value))
{
dr.Read();
LoadProperty(IdProperty, dr.GetInt32("Id"));
LoadProperty(NameProperty, dr.GetString("Name"));
}
}
}
3使用对象工厂方法
public class CustomerFactory : ObjectFactory
{
public object Fetch(SingleCriteria<Customer, int> criteria)
{
using (var ctx = ConnectionManager<SqlConnection>.GetManager("MyDb"))
{
using (var cm = ctx.Connection.CreateCommand())
{
cm.CommandType = CommandTypes.Text;
cm.CommandText = "SELECT Id, Name FROM Customer WHERE id=@id";
cm.Parameters.AddWithValue("@id", criteria.Value);
using (var dr = new SafeDataReader(cm.ExecuteReader()))
{
dr.Read();
var result = (BusinessLibrary.CustomerEdit)Activator.CreateInstance(
typeof(BusinessLibrary.CustomerEdit), true);
MarkNew(result);
result.LoadData(dr);
return result;
}
}
}
}
}
public void LoadData(SafeDataReader dr)
{
LoadProperty(IdProperty, dr.GetInt32("Id"));
LoadProperty(NameProperty, dr.GetString("Name"));
}
这种方法会形成循环引用,既DAL类要引用业务对象类,因为要对业务对象类的属性赋值。业务对象类要引用DAL中,因为要使用DAL来访问数据库。我们一般喜欢DAL与具体业务无关,业务对象类要引用DAL,而不是相互引用。
LoadData还有一种形式,就是DAL返回的是强类型的数据
internal void LoadData(Customer data)
{
LoadProperty(IdProperty, data.Id);
LoadProperty(NameProperty, data.Name);
}
我不喜欢这种方式,这样会造成数据二次组装,第一次是从数据库中读出数据构造强类型的Customer,Customer就一个数据库实体类,一般叫做数据传输类(DTO)。第二次是构造业务对象的属性。我觉得这是种浪费。特别是在返回业务对象集合,要使用二次循环才能得到数据。数据库阶段构造的强类型对我用处不大。
DTO只用于数据传输,是不能与UI进行交互的。这与PetShop中的Model不一样。
具体怎么选择要结合具体的系统,主要考虑以下方面
l 性能
l 封装性
l 分层
l 复杂性
我选择的是第2种方式,并且DAL返回的类型为SafeDataReader这种方式。因为强类型对我来说用处不大,我们开发时不会把数据访问层由单独的一个人来完成,业务对象由另一个人完成。我想一般的应用这两层都是一个人来完成,所以他对数据库实现和业务实现都清楚,不会搞错字段的名字和用意,在说使用自动化的生成工具,可以减少很多人为错误,特别是数据库字段与业务属性间的对应的关系。这种方式也兼顾了性能的考虑,不要在DAL中做太多与数据访问无关的事。也可以考虑返回DataSet对象,我看CSLA中给的例子都是用SafeDataReader,而DataReader是性能最好的。
这种方式也保证了业务类、数据访问类各自的封装性。不会因为构造DTO类而重复读写数据。但这种方式与采用DTO类来说,增加了性能,但减少了灵活性。
对不同的数据库还会有不同的选择,因为目前.Net对SQL Server支持的好,又有LINQ TO SQL 和ADO.NET Entity Framework。如果你只在SQL Serve数据库上开发DAL层就特别方便了,如果采用LINQ TO SQL 或ADO.NET Entity Framework,DAL就自动有具有强类型,也不用写SQL语句,灵活性相当大。
我们主要使用Oracle数据库,所以还是采用DAL返回DataReader的方式。现在也有ADO.NET Entity Framework For Oracle的版本。我对这些东西不太感冒。还是喜欢自己写SQL,自己的控制方法会更灵活。毕竟我们的业务系统会很复杂。