实体框架6和组的工作......在哪里,什么时候?是像在ado.net交易? [英] Entity Framework 6 and Unit Of Work... Where, When? Is it like transactions in ado.net?

查看:113
本文介绍了实体框架6和组的工作......在哪里,什么时候?是像在ado.net交易?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

创建一个新的MVC项目,就像在数据层库的想法,所以我已经实现了他们。我还创建了一个服务层来处理所有的业务逻辑和验证,这一层又使用适当的存储库。像这样的东西(我用简单的注射器注入)

Creating a new MVC project and like the idea of repositories in the data layer, so i have implemented them. I have also created a Service layer to handle all business logic and validation, this layer in turn uses the appropriate repository. Something like this (I am using Simple Injector to inject)

DAL层

public 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 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);
    }
}

现在,这是非常简单的,所以我可以使用下面的code更新的对象。

Now this is very simple, so i can use the following code to update an object.

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

这还是创建一个对象

Or this to create an object

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

现在说我需要更新基础上的又一创造ID的项目,我可以用code高于一切正常,但如果出现错误,会发生什么?我需要某种形式的交易/回滚。请问这是什么工作模式的单位是想解决的?

Now say i needed to update an item based on the creation Id of another, i could use the code above all fine, but what happens if an error occurs? I need some sort of transaction/rollback. Is this what the Unit Of Work pattern is suppose to solve?

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

So i guess i need to have DbContext in my UnitOfWork object, so i create an object like so?

public UnitOfWork() : IDisposable {

    private DbContext _context;

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

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

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

}

好了,所以再一次,那是相当简单。持有的UnitOfWork内以及(我反正所有的仓库使用相同的上下文中),并调用调用SaveChanges()方法。然后我会从我的资源库中删除调用SaveChanges()方法调用。所以要加我会做到以下几点:

Ok so again, thats quite simple. UnitOfWork holds the context as well ( i use same context on all repositories anyway) and it calls the SaveChanges() method. I would then remove the SaveChanges() method call from my repository. So to add i would do the following:

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的其他对象,这将现在不行,因为标识也不会被创建,直到我调用commit的UOW。示例

But what if i need to create an object and then update other objects based on that Id, this will now not work, because the Id will not be created until i call Commit on the uow. Example

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类是不正确的......也许我错过理解的东西。

So i have a feeling that this UnitOfWork class is not right... maybe i am miss understanding something.

我需要能够添加一个实体,并得到了Id和使用它的另一个实体,但是,如果发生错误,我想回退像一个ado.net的事务会怎么做。

I need to be able to add an entity and get that Id and use it on another entity, but if an error occurs, i want to "rollback" like an ado.net transaction would do.

时使用实体框架和库这个功能可能吗?

Is this functionality possible using Entity Framework and Repositories?

推荐答案

我不得不说,先说的没有的解决这一问题的唯一正确途径。我只是presenting这里我可能会做。

I have to say first that there is not a unique right way to solve this issue. I'm just presenting here what I would probably do.

的第一件事是, 的DbContext 本身实现工作模式的单位。调用的SaveChanges 确实建立DB交易所以对DB执行的每个查询将被rollbacked是出了问题。

First thing is, DbContext itself implements the Unit of work pattern. Calling SaveChanges does create a DB transaction so every query executed against the DB will be rollbacked is something goes wrong.

现在,没有在你当前设计的一个主要问题:你的资料库调用的SaveChanges 的DbContext 。这意味着,你让 XXXRepository 负责提交你的工作单位上进行的所有的修改,不仅仅是修改的XXX实体资源库负责。

Now, there is a major issue in the current design you have: your repository calls SaveChanges on the DbContext. This means that you make XXXRepository responsible to commit all the modification you made on the unit of work, not just the modifications on the XXX entities your repository is responsible for.

另一件事是,的DbContext 是库本身了。所以,抽象另一个存储库中的的DbContext 使用仅仅是创建一个现有的抽象另一个抽象,这只是太多code IMO。

Another thing is that DbContext is a repository itself too. So abstracting the DbContext usage inside another repository just creates another abstraction on an existing abstraction, that's just too much code IMO.

另外一个事实,你可能需要从YYY库和XXX库YYY实体访问XXX实体,因此要避免你会得到一个无用的 MyRepository最终循环依赖:IRepository&LT; TEntity&GT; 刚复制所有的 DbSet 方法。

Plus the fact you may need to access XXX entities from YYY repository and YYY entities from XXX repository, so to avoid circular dependencies you'll end up with a useless MyRepository : IRepository<TEntity> that just duplicates all the DbSet methods.

我会放弃整个库层。我会直接使用的DbContext 服务层内。当然,你可以考虑您不希望在服务层来复制所有复杂的查询。是这样的:

I would drop the whole repository layer. I would use the DbContext directly inside the service layer. Of course, you can factor all complex queries you don't want to duplicate in the service layer. Something like:

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 是事务性的。

With this pattern, every public call on a service class is executed inside a transaction thanks to DbContext.SaveChanges being transactional.

现在你有与被第一实体插入后所需要的ID的例子中,一种解决方案是不使用ID,但该实体本身。所以,你让实体框架和它自己的实现工作模式的交易单位的吧。

Now for the example you have with the ID that is required after the first entity insertion, one solution is to not use the ID but the entity itself. So you let Entity Framework and its own implementation of the unit of work pattern deal with it.

而不是:

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();

如果你没有一个导航属性​​,或者如果你有一个更复杂的用例来处理,该解决方案是使用你的公共服务方法内良好的黄金交易:

If you don't have a navigation property, or if you have a more complex use case to deal with, the solution is to use the good gold transaction inside your public service method:

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;
        }
    }
}

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

You can even call multiple services method inside a transactional scope if required:

public class MyController()
{
    ...

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

            transaction.Commit();
        }
    }
}

这篇关于实体框架6和组的工作......在哪里,什么时候?是像在ado.net交易?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆