本文介绍了UnitOfWork与Unity和实体框架的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Unity来管理我的服务层,这反过来说是管理所有仓库的UnitOfWork。

Hi I am using Unity to manage my service layers, which in turn speak to UnitOfWork which manages all the repositories.

我的一些服务叫其他服务,我的问题是我如何在服务层之间通过相同的UnitOfWork?

Some of my services call other services, my question is how can i pass the same UnitOfWork between service layers?

在我的情况下,所有控制器操作都是从GUI上启动的,每个按钮的操作或事件在定时器上,是为什么我有一个工厂来按需创建UnitOfWork,但是由于我不知道如何在服务之间传递这个UnitOfWork,所以导致问题。

In my case all controller actions are initiated from a GUI on each button action or event on a timer, this is why I have a factory to create UnitOfWork on demand, but it is causing issues as i dont know how to pass this UnitOfWork between services.

特别困难的是知道如何将这个特定的UnitOfWork实例注入到服务构造函数中。请注意,一些服务可能会长时间运行(背景线程10分钟左右),我不知道是否对设计有任何影响。

Especially difficult is knowing how to get this specific UnitOfWork instance injected into the service constructor. Please note that some of the services may be long running (10 minutes or so on a background thread), i don't know if that has any impact on the design or not.

目前,从其他服务调用的服务正在创建自己的UnitOfWork,这会导致事务设计和实体框架实体跟踪的问题。

Currently the service that is called from the other service is then creating its own UnitOfWork which is causing issues for both transactional design, and Entity framework entity tracking.

建议非常欢迎!

class OtherService : IOtherService
{
    public OtherService(IUnitOfWorkFactory unitOfworkFactory, 
        ISettingsService settingsService)
    {
        UnitOfWorkFactory = unitOfworkFactory;
        SettingsService = settingsService;
    }
    IUnitOfWorkFactory UnitOfWorkFactory;
    ISettingsService SettingsService;

    function SomeSeviceCall()
    {
        // Perhaps one way is to use a factory to instantiate a 
        // SettingService, and pass in the UnitOfWork here?
        // Ideally it would be nice for Unity to handle all of 
        // the details regardless of a service being called from
        // another service or called directly from a controller
        // ISettingsService settingsService = 
        //     UnityContainer.Resolve<ISettingService>();

        using (var uow = UnitOfWorkFactory.CreateUnitOfWork())
        {
            var companies = uow.CompaniesRepository.GetAll();
            foreach(Company company in companies)
            {
                settingsService.SaveSettings(company, "value");
                company.Processed = DateTime.UtcNow();
            }
            uow.Save();
        }
    }
}

class SettingsService : ISettingsService
{
    public SettingsService(IUnitOfWorkFactory unitOfworkFactory)
    {
        UnitOfWorkFactory = unitOfworkFactory;
    }
    IUnitOfWorkFactory UnitOfWorkFactory;

    // ISettingsService.SaveSettings code in another module...
    function void ISettingsService.SaveSettings(Company company, 
        string value)
    {
        // this is causing an issue as it essentially creates a 
        // sub-transaction with the new UnitOfWork creating a new 
        // Entiy Framework context
        using (var uow = UnitOfWorkFactory.CreateUnitOfWork())
        {
            Setting setting = new Setting();
            setting.CompanyID = company.CompanyID;
            setting.SettingValue = value;
            uow.Insert(setting);
            uow.Save();
        }
    }
}


推荐答案

我一直在争取这个问题,这是我想出来的...

Hi I've been battling with this problem this is what I've come up with...

public class UnitOfWorkFactory
{
    private static readonly Hashtable _threads = new Hashtable();
    private const string HTTPCONTEXTKEY = 
        "AboutDbContext.UnitOfWorkFactory";

    public static IUnitOfWork Create()
    {
        IUnitOfWork unitOfWork = GetUnitOfWork();

        if (unitOfWork == null || unitOfWork.IsDisposed)
        {
            unitOfWork = new UnitOfWork();
            SaveUnitOfWork(unitOfWork);
        }
        return unitOfWork;
    }

    public static IUnitOfWork GetUnitOfWork()
    {
        if (HttpContext.Current != null)
        {
            if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
            {
                return (IUnitOfWork)HttpContext
                    .Current.Items[HTTPCONTEXTKEY];
            }
            return null;
        }

        var thread = Thread.CurrentThread;

        if (string.IsNullOrEmpty(thread.Name))
        {
            thread.Name = Guid.NewGuid().ToString();
            return null;
        }

        lock (_threads.SyncRoot)
        {
            return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
        }
    }

    private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
    {
        if (HttpContext.Current != null)
        {
            HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
        }
        else
        {
            lock (_threads.SyncRoot)
            {
                _threads[Thread.CurrentThread.Name] = unitOfWork;
            }
        }
    }

    public static void DisposeUnitOfWork(IUnitOfWork unitOfWork)
    {
        if (HttpContext.Current != null)
        {
            HttpContext.Current.Items.Remove(HTTPCONTEXTKEY);
        }
        else
        {
            lock (_threads.SyncRoot)
            {
                _threads.Remove(Thread.CurrentThread.Name);
            }
        }
    }
}

public interface IUnitOfWork : IDisposable
{
    void Commit();
    bool IsDisposed { get; }
}

public class UnitOfWork : MyContext
{

}

public abstract class Repository<T>
    : IRepository<T>, IDisposable where T : class
{
    private UnitOfWork _context;

    private UnitOfWork Context
    {
        get
        {
            if (_context == null || _context.IsDisposed)
                return _context = GetCurrentUnitOfWork<UnitOfWork>();

            return _context;
        }
    }

    public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() 
        where TUnitOfWork : IUnitOfWork
    {
        return (TUnitOfWork)UnitOfWorkFactory.GetUnitOfWork();
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
    {
        return Context.Set<T>().Where(predicate).ToList();
    }

    public bool Exists(Expression<Func<T, bool>> predicate)
    {
        return Context.Set<T>().Any(predicate);
    }

    public T First(Expression<Func<T, bool>> predicate)
    {
        return Context.Set<T>().Where(predicate).FirstOrDefault();
    }

    public IEnumerable<T> GetAll()
    {
        return Context.Set<T>().ToList();
    }

    public IEnumerable<T> GetAllOrderBy(Func<T, object> keySelector)
    {
        return Context.Set<T>().OrderBy(keySelector).ToList();
    }

    public IEnumerable<T> GetAllOrderByDescending(Func<T, object> keySelector)
    {
        return Context.Set<T>().OrderByDescending(keySelector).ToList();
    }

    public void Commit()
    {
        Context.SaveChanges();
    }

    public void Add(T entity)
    {
        Context.Set<T>().Add(entity);
    }

    public void Update(T entity)
    {
        Context.Entry(entity).State = EntityState.Modified;
    }

    public void Delete(T entity)
    {
        Context.Set<T>().Remove(entity);
    }

    public void Dispose()
    {
        if (Context != null)
        {
            Context.Dispose();
        }
        GC.SuppressFinalize(this);
    }
}

public class MyContext : DbContext, IUnitOfWork
{
    public DbSet<Car> Cars { get; set; }

    public void Commit()
    {
        SaveChanges();
    }

    protected override void Dispose(bool disposing)
    {
        IsDisposed = true;
        UnitOfWorkFactory.DisposeUnitOfWork(this);
        base.Dispose(disposing);
    }

    public bool IsDisposed { get; private set; }
}

然后我可以做:

using (var unitOfWork = UnitOfWorkFactory.Create())
{
    _carRepository.Add(new Car
    {
        Make = "Porshe", Name = "Boxter"
    });

    _carRepository.Commit();
}

这篇关于UnitOfWork与Unity和实体框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-25 18:56