1

您好我正在使用Unity来管理我的服务层,这反过来又与管理所有存储库的UnitOfWork对话。UnitOfWork with Unity and Entity Framework

我的一些服务调用其他服务,我的问题是如何在服务层之间传递相同的UnitOfWork?

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

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

目前,从其他服务调用的服务正在创建自己的UnitOfWork,这对于事务设计和实体框架实体跟踪都造成了问题。

建议非常欢迎!

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(); 
     } 
    } 
} 
+1

看看[这个问题](http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why)。我认为[选定的答案](http://stackoverflow.com/a/10588594/264697)也解决您的问题。 – Steven

+0

@Steven谢谢,我喜欢命令/处理程序设计的想法,如何通过命令装饰器跨多个服务共享上下文?如果你有被调用的装饰器的任何代码示例和返回的数据具有巨大的价值。 – g18c

+0

我错过了使用Unity的事实。对于Unity来说,注册通用装饰器是非常困难的(尽管你可能会使它与Unity的拦截支持一起工作)。然而,从理论上讲,它只是定义一个装饰器(比如'TransactionCommandHandlerDecorator'),它在获得一个工作单元后,在调用'decorated.Handle(command)之后,在'Handle'方法中调用'uwo.Save'。 '。您注册这个装饰器以包裹您的命令处理程序,并以“每个Web请求”生活方式注册工作单元。 – Steven

回答

2

您好我一直在这个问题对抗,这是我想出...

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

您可以使用某种与当前线程绑定并在服务代码中明确解析的“当前”工作单元。你需要class来保存UoW的线程静态实例来实现这一点。但是,这不是很好的解决方案。

-1

你是法官.. 我觉得你是双重做的。

点1: http://www.britannica.com/topic/Occams-razor

点2: 从EF主要对象,对的DbContext的F2对象浏览器描述...


公共类的DbContext System.Data成员。实体

摘要: DbContext实例表示工作单位的组合存储库这样的模式可用于从数据库中进行查询并将所做的更改分组在一起,然后将这些更改作为一个单元写回商店。