数据访问层(Data Access Layer)负责与数据储存设备打交道,为业务层提供数据服务(一般指增、删、改、查)。一个好的数据访问层可在不影响其他逻辑的情况下,替换数据访问技术、数据据库。
数据访问层的常见模式与原则
- 工作单元(Unit of Work)
维护一系列操作的事务性(Transaction),一系列操作要么都成功,如果有一个操作失败,则事务回滚。这里也主要用于对数据库的操作。
如果通过sql
脚本直接访问数据库,可以直接用sql
调用相应数据库的事务。
如果采用自己写的ORM,则可定义一个IUnitOfWork
接口,将增、删、改的数据保存进集合,在Commit
方法将object数据存储数据库,可通过TransactionScope
实现事务使用方式连接。
public interface IUnitOfWork
{
void RegisterAmended(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
void RegisterNew(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
void Commit();
}
如果利用第三方ORM库,一般都提供Unit of work
- 标识映射
标识映射即通过唯一标识,将加载的数据存入缓存,方便下次访问。 - 延迟加载 表示需要时才去加载所需的资源。一般指需要时采取加载外键关联的表。如在学生管理系统,每个学生都有自己的成绩,在展示学生列表时不加载成绩,只有进入某个学生详细信息才加载。
- 数据并发控制
同时修改一份数据造成修改冲突,一般通过为数据添加一个version
属性表明版本。
EntityFramework
EntityFramework是微软官方提供的ORM类库,其采用Repository
模型,功能强大,这里简单介绍下其数据访问层特性的实现。
工作单元(Unit of Work)
- 隐式事务
using (var context = new MyStoreContext())
{
customer = new Customer { FirstName = request.FirstName, LastName = request.LastName };
context.Customers.Add(customer);
context.SaveChanges();
return customer;
}
其中context.SaveChanges()
方法事务执行,其内部实现了事务。
2. 显示事务
using (var context = new BloggingContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();
var blogs = context.Blogs
.OrderBy(b => b.Url)
.ToList();
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
}
- Cross-context transaction(跨库的事务)
using (var context1 = new BloggingContext(connectionstring1))))
{
using (var transaction = context1.Database.BeginTransaction())
{
try
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context1.SaveChanges();
using (var context2 = new BloggingContext(connectionstring2))
{
//context2 用context1的事务
context2.Database.UseTransaction(transaction.GetDbTransaction());
var blogs = context2.Blogs
.OrderBy(b => b.Url)
.ToList();
}
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
延迟性加载
EntityFramework 默认为延迟加载,如果希望饥渴加载需要include
数据并发控制
为实体添加version
属性
[Timestamp]
public byte[] RowVersion { get; set; }
通过异常机制查询保存的数据是否被修改或删除,异常为DbUpdateConcurrencyException