gpt4 book ai didi

c# - Entity Framework 6 和工作单元……何时何地?是不是很像ado.net中的交易?

转载 作者:IT王子 更新时间:2023-10-29 04:08:04 26 4
gpt4 key购买 nike

创建一个新的 MVC 项目并且喜欢数据层中存储库的想法,所以我已经实现了它们。我还创建了一个服务层来处理所有业务逻辑和验证,该层又使用适当的存储库。像这样(我用的是Simple Injector来注入(inject))

DAL 层

public class MyRepository {

private DbContext _context;
public MyRepository(DbContext context) {
_context = context;
}

public MyEntity Get(int id)
{
return _context.Set<MyEntity>().Find(id);
}

public TEntity Add(MyEntity t)
{
_context.Set<MyEntity>().Add(t);
_context.SaveChanges();
return t;
}

public TEntity Update(MyEntity updated, int key)
{
if (updated == null)
return null;

MyEntity existing = _context.Set<MyEntity>().Find(key);
if (existing != null)
{
_context.Entry(existing).CurrentValues.SetValues(updated);
_context.SaveChanges();
}
return existing;
}

public void Delete(MyEntity t)
{
_context.Set<MyEntity>().Remove(t);
_context.SaveChanges();
}
}

服务层

public class MyService {
private MyRepository _repository;

public MyService(MyRepository repository) {
_repository = repository;
}

public MyEntity Get(int id)
{
return _repository.Get(id);
}

public MyEntity Add(MyEntity t)
{
_repository.Add(t);

return t;
}

public MyEntity Update(MyEntity updated)
{
return _repository.Update(updated, updated.Id);
}

public void Delete(MyEntity t)
{
_repository.Delete(t);
}
}

现在这很简单,所以我可以使用以下代码来更新对象。

MyEntity entity = MyService.Get(123);
MyEntity.Name = "HELLO WORLD";
entity = MyService.Update(entity);

或者这个来创建一个对象

MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);
// entity.Id is now populated

现在说我需要根据另一个项目的创建 ID 更新一个项目,我可以使用上面的代码一切正常,但是如果发生错误会怎样?我需要某种交易/回滚。这是工作单元模式要解决的问题吗?

所以我想我需要在我的 UnitOfWork 对象中包含 DbContext,所以我创建了一个这样的对象?

public class UnitOfWork : IDisposable {

private DbContext _context;

public UnitOfWork(DbContext context) {
_context = context;
}

public Commit() {
_context.SaveChanges();
}

public Dispose() {
_context.Dispose();
}

}

好吧,这很简单。 UnitOfWork 也保存上下文(无论如何我在所有存储库上使用相同的上下文)并且它调用 SaveChanges() 方法。然后我会从我的存储库中删除 SaveChanges() 方法调用。所以要添加我会做以下事情:

UnitOfWork uow = new UnitOfWork(new DbContext()); // i would inject this somehow

MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);

uow.Commit();

但是如果我需要创建一个对象然后根据该 Id 更新其他对象,这将不起作用,因为在我调用 uow 上的 Commit 之前不会创建 Id。示例

UnitOfWork uow = new UnitOfWork(new DbContext()); // i would inject this somehow

MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);
// entity.Id is NOT populated

MyEntity otherEntity = MyService.Get(123);
otherEntity.OtherProperty = entity.Id;
MyService.Update(otherEntity);

uow.Commit(); // otherEntity.OtherProperty is not linked.....?

所以我觉得这个 UnitOfWork 类不对...也许我想念某些东西。

我需要能够添加一个实体并获取该 Id 并将其用于另一个实体,但如果发生错误,我想像 ado.net 事务一样“回滚”。

使用 Entity Framework and Repositories 是否可以实现此功能?

最佳答案

我必须首先声明,没有唯一正确的方法来解决这个问题。我只是在这里展示我可能会做的事情。


首先, DbContext本身实现了工作单元模式。打电话SaveChanges 确实创建了一个数据库事务,以便在出现问题时回滚对数据库执行的每个查询。

现在,您的当前设计存在一个主要问题:您的存储库调用 SaveChangesDbContext 上.这意味着你制作 XXXRepository负责提交您对工作单元所做的所有修改,而不仅仅是对您的存储库负责的 XXX 实体的修改。

另一件事是DbContext本身也是一个存储库。所以抽象出 DbContext在另一个存储库中使用只会在现有抽象上创建另一个抽象,IMO 的代码太多了。

此外,您可能需要访问 YYY 存储库中的 XXX 实体和 XXX 存储库中的 YYY 实体,因此为了避免循环依赖,您最终会得到无用的 MyRepository : IRepository<TEntity>。只是复制了所有 DbSet方法。

我会删除整个存储库层。我会使用 DbContext直接在服务层内。当然,您可以将所有不想在服务层中复制的复杂查询分解为因素。像这样的东西:

public MyService()
{
...
public MyEntity Create(some parameters)
{
var entity = new MyEntity(some parameters);
this.context.MyEntities.Add(entity);

// Actually commits the whole thing in a transaction
this.context.SaveChanges();

return entity;
}

...

// Example of a complex query you want to use multiple times in MyService
private IQueryable<MyEntity> GetXXXX_business_name_here(parameters)
{
return this.context.MyEntities
.Where(z => ...)
.....
;
}
}

使用这种模式,由于 DbContext.SaveChanges,对服务类的每个公共(public)调用都在事务内执行具有交易性。

现在,对于第一个实体插入后需要 ID 的示例,一个解决方案是不使用 ID,而是使用实体本身。所以你让 Entity Framework 和它自己的工作单元模式实现来处理它。

所以代替:

var entity = new MyEntity();
entity = mydbcontext.Add(entity);
// what should I put here?
var otherEntity = mydbcontext.MyEntities.Single(z => z.ID == 123);
otherEntity.OtherPropertyId = entity.Id;

uow.Commit();

你有:

var entity = new MyEntity();
entity = mydbcontext.Add(entity);

var otherEntity = mydbcontext.MyEntities.Single(z => z.ID == 123);
otherEntity.OtherProperty = entity; // Assuming you have a navigation property

uow.Commit();

如果您没有导航属性,或者如果您有更复杂的用例要处理,解决方案是在您的公共(public)服务方法中使用 good gold transaction:

public MyService()
{
...
public MyEntity Create(some parameters)
{
// Encapuslates multiple SaveChanges calls in a single transaction
// You could use a ITransaction if you don't want to reference System.Transactions directly, but don't think it's really useful
using (var transaction = new TransactionScope())
{
var firstEntity = new MyEntity { some parameters };
this.context.MyEntities.Add(firstEntity);

// Pushes to DB, this'll create an ID
this.context.SaveChanges();

// Other commands here
...

var newEntity = new MyOtherEntity { xxxxx };
newEntity.MyProperty = firstEntity.ID;
this.context.MyOtherEntities.Add(newEntity);

// Pushes to DB **again**
this.context.SaveChanges();

// Commits the whole thing here
transaction.Commit();

return firstEntity;
}
}
}

如果需要,您甚至可以在事务范围内调用多个服务方法:

public class MyController()
{
...

public ActionResult Foo()
{
...
using (var transaction = new TransactionScope())
{
this.myUserService.CreateUser(...);
this.myCustomerService.CreateOrder(...);

transaction.Commit();
}
}
}

关于c# - Entity Framework 6 和工作单元……何时何地?是不是很像ado.net中的交易?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26055497/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com