Entity Framework、导航属性、存储库、工作单元、可变 ORM
Entity Framework,Navigation properties,Repository,Unit Of Work,Changeable ORM
我正在尝试为我的应用程序创建一个带有存储库和工作单元模式的新核心框架(主要是网络),我可以稍后将我的 ORM 更改为 NHibernate 或 Dapper。
现在我的工作单元界面是这样的:
public interface IUnitOfWork : IDisposable
{
void Commit();
void Rollback();
}
而Entity Framework实现是这样的(为便于阅读而修剪)
public class EfUnitOfWork : IUnitOfWork
{
....
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
public void Commit()
{
this._context.SaveChanges(true);
this._transaction.Commit();
...
}
public void Rollback()
{ ...
}
}
问题是在我的包含业务逻辑的服务层中,我可以使用导航属性做这样的事情:
public bool CreateCity(CityCreateModel model)
{
using (var uow = _unitOfWorkFactory.Create())
{
var city = new City();
city.Name = model.Name;
city.State = new State() { Country = new Country() { Name = "SomeCountry" }, Name = "SomeCity" };
_cityRepository.Create(city);
try
{
uow.Commit();
return true;
}
catch (Exception)
{
uow.Rollback();
throw;
}
}
}
存储库创建方法非常简单,因为我使用 entity framework :
public void Create(City entity)
{
_set.Add(entity);
}
问题从这里开始,当团队成员通过在导航属性上使用 new 关键字或为集合导航属性添加项目来编写类似服务示例的代码时,entity framework 检测到这些更改,并且当我保存更改时,这些也保存到数据库中。
如果我稍后尝试将现有示例更改为 Dapper.NET 或更改为 REST 服务,可能会出现很多问题,我必须去寻找每个导航 属性 并跟踪它们是否已更改并为它们编写了很多(可能是垃圾)代码,因为我真的不知道通过 entity framework 在 table 上插入了什么以及没有插入什么(因为导航属性也是在我上面的示例中,我的存储库仅调用了一次用于 City 的 1 个插入)
有没有一种方法可以防止这种行为,或者是否有一种已知的模式可以让我尽早适应,这样我以后就不会遇到问题了?
你是如何克服这个问题的?
在开始之前,我想对您的代码做一些说明:
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
1) 从您的示例中我可以看出您正在共享相同的 DbContext(在整个应用程序的构造函数中作为参数给出。我认为这不是一个好主意,因为实体将缓存在一级缓存和更改跟踪器将跟踪它们。使用这种方法,当数据库增长时,很快就会出现性能问题。
_cityRepository.Create(city);
public void Create(City entity)
{
_set.Add(entity);
}
2) 基本存储库应该是 T 类型的泛型,其中 T 是一个实体!这样你就可以创建一个城市了;
var city = _cityRepository.Create();
Fill the city or provide the data as parameters in the create method.
回到你的问题:
Is there a way to prevent this behavior or is there a pattern known that i can adapt early on so i won't have problems later on?
每个 ORM 都有自己的设计概念,要找到适合它们的通用方法并不容易,我将执行以下操作:
1) 将存储库合约分离为一个程序集(合约 dll)
2) 对于每个 ORM 框架,使用一个单独的程序集来实现存储库契约。
示例:
public interface ICityRepository<City> :IGenericRepsotiory<City>
{
City Create();
Find();
....
}
实体框架程序集:
public class CityRepositoryEF : ICityReposiory
{
..
Dapper 框架组件:
public class CityRepositoryDapper : ICityReposiory
{
..
如果您按照下面的 URL 进行操作,您可以找到精彩的演练。它由 entity framework 布道者朱莉·勒曼 (Julie Lerman) 撰写。
http://thedatafarm.com/data-access/agile-entity-framework-4-repository-part-1-model-and-poco-classes/
我正在尝试为我的应用程序创建一个带有存储库和工作单元模式的新核心框架(主要是网络),我可以稍后将我的 ORM 更改为 NHibernate 或 Dapper。
现在我的工作单元界面是这样的:
public interface IUnitOfWork : IDisposable
{
void Commit();
void Rollback();
}
而Entity Framework实现是这样的(为便于阅读而修剪)
public class EfUnitOfWork : IUnitOfWork
{
....
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
public void Commit()
{
this._context.SaveChanges(true);
this._transaction.Commit();
...
}
public void Rollback()
{ ...
}
}
问题是在我的包含业务逻辑的服务层中,我可以使用导航属性做这样的事情:
public bool CreateCity(CityCreateModel model)
{
using (var uow = _unitOfWorkFactory.Create())
{
var city = new City();
city.Name = model.Name;
city.State = new State() { Country = new Country() { Name = "SomeCountry" }, Name = "SomeCity" };
_cityRepository.Create(city);
try
{
uow.Commit();
return true;
}
catch (Exception)
{
uow.Rollback();
throw;
}
}
}
存储库创建方法非常简单,因为我使用 entity framework :
public void Create(City entity)
{
_set.Add(entity);
}
问题从这里开始,当团队成员通过在导航属性上使用 new 关键字或为集合导航属性添加项目来编写类似服务示例的代码时,entity framework 检测到这些更改,并且当我保存更改时,这些也保存到数据库中。
如果我稍后尝试将现有示例更改为 Dapper.NET 或更改为 REST 服务,可能会出现很多问题,我必须去寻找每个导航 属性 并跟踪它们是否已更改并为它们编写了很多(可能是垃圾)代码,因为我真的不知道通过 entity framework 在 table 上插入了什么以及没有插入什么(因为导航属性也是在我上面的示例中,我的存储库仅调用了一次用于 City 的 1 个插入)
有没有一种方法可以防止这种行为,或者是否有一种已知的模式可以让我尽早适应,这样我以后就不会遇到问题了?
你是如何克服这个问题的?
在开始之前,我想对您的代码做一些说明:
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
1) 从您的示例中我可以看出您正在共享相同的 DbContext(在整个应用程序的构造函数中作为参数给出。我认为这不是一个好主意,因为实体将缓存在一级缓存和更改跟踪器将跟踪它们。使用这种方法,当数据库增长时,很快就会出现性能问题。
_cityRepository.Create(city);
public void Create(City entity)
{
_set.Add(entity);
}
2) 基本存储库应该是 T 类型的泛型,其中 T 是一个实体!这样你就可以创建一个城市了;
var city = _cityRepository.Create();
Fill the city or provide the data as parameters in the create method.
回到你的问题:
Is there a way to prevent this behavior or is there a pattern known that i can adapt early on so i won't have problems later on?
每个 ORM 都有自己的设计概念,要找到适合它们的通用方法并不容易,我将执行以下操作:
1) 将存储库合约分离为一个程序集(合约 dll)
2) 对于每个 ORM 框架,使用一个单独的程序集来实现存储库契约。
示例:
public interface ICityRepository<City> :IGenericRepsotiory<City>
{
City Create();
Find();
....
}
实体框架程序集:
public class CityRepositoryEF : ICityReposiory { ..
Dapper 框架组件:
public class CityRepositoryDapper : ICityReposiory { ..
如果您按照下面的 URL 进行操作,您可以找到精彩的演练。它由 entity framework 布道者朱莉·勒曼 (Julie Lerman) 撰写。 http://thedatafarm.com/data-access/agile-entity-framework-4-repository-part-1-model-and-poco-classes/