IoC 实例化困境共享上下文

发布于 2024-12-13 03:53:23 字数 1994 浏览 0 评论 0原文

我有一个想要与存储库共享的数据上下文。大多数实现都推荐使用单例容器,因为它节省了每次重建整个容器的费用。问题是您需要为每个相关的请求集构建一个全新的容器及其所有依赖项,例如:

 class CompanyService : ICompanyService
{
    private IUserRepository _userRepository;

    private IEmployeeRepository _employeeRepository;

    public CompanyService(IEmployeeRepository employeeRepository,
          IUserRepository userRepository)
    {
        this._employeeRepository = employeeRepository;
        this._userRepository = userRepository;
    }
}

公司服务需要用户和员工存储库,这些存储库中的每一个都需要一个上下文:

class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(IDataContext dataContext) : base(dataContext) { }
}

class EmployeeRepository : Repository<Employee>, IEmployeeRepository
{
    public EmployeeRepository(IDataContext dataContext) : base(dataContext) { }
}

我希望能够注册这些类型容器如下:

    public WindsorContainer BuildContainer()
    {
        var container = new WindsorContainer();
        container.Register(Component.For<SqlContext, IDataContext>());
        container.Register(Component.For<UserRepository, IUserRepository>());
        container.Register(Component.For<EmployeeRepository, IEmployeeRepository>());
        container.Register(Component.For<CompanyService, ICompanyService>());
        return container;
    }

然后我只想能够按如下方式解析服务:

        var container = BuildContainer();
        var service = container.Resolve<ICompanyService>();

我知道默认实例是单例,我希望包括存储库在内的所有这些服务都是暂时的,但我想与存储库共享相同的上下文但仅适用于特定的一组请求。

编辑:ThreadLocal 或基于请求的实例将不起作用,因为当我从同一个线程解析 ICompanyService 两次但在两个上下文中使用时,线程保持相同,并且 IDataContext 的实例将相同,但它们不应该相同。

我看不到在容器是单例的情况下执行此操作的方法,我看到的唯一方法是使用构建函数在每个请求上重建整个容器,并使 IDataContext 成为单例,而其他所有内容都是瞬态的。

此方法的另一个问题是我无法为我打算成为单例的更昂贵的服务重建此容器。不知何故,我需要将容器分成两部分:一个包含昂贵服务或我不想重建的服务的主容器,然后是一个可以在构建函数中使用的派生容器,该容器将扩展主容器并处理上下文特定的请求。

我是否只是错误地看待整个问题,有人能指出我正确的方向吗?

谢谢

I have a datacontext that i want to share with repositories. Most implementations recomment a singleton container as it saves the expense of rebuilding the entire container each time. The problem is you need to build an entire new container and all its dependencies for each related set of requests for instance:

 class CompanyService : ICompanyService
{
    private IUserRepository _userRepository;

    private IEmployeeRepository _employeeRepository;

    public CompanyService(IEmployeeRepository employeeRepository,
          IUserRepository userRepository)
    {
        this._employeeRepository = employeeRepository;
        this._userRepository = userRepository;
    }
}

Company service requires User and employee repositories, each one of these repositories requires a context:

class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(IDataContext dataContext) : base(dataContext) { }
}

class EmployeeRepository : Repository<Employee>, IEmployeeRepository
{
    public EmployeeRepository(IDataContext dataContext) : base(dataContext) { }
}

I want to be able to register these types with the container as follows:

    public WindsorContainer BuildContainer()
    {
        var container = new WindsorContainer();
        container.Register(Component.For<SqlContext, IDataContext>());
        container.Register(Component.For<UserRepository, IUserRepository>());
        container.Register(Component.For<EmployeeRepository, IEmployeeRepository>());
        container.Register(Component.For<CompanyService, ICompanyService>());
        return container;
    }

Then I just want to be able to resolve the Service as follows:

        var container = BuildContainer();
        var service = container.Resolve<ICompanyService>();

I know the default instancing is singleton, I want all these services including repositories to be transient, but i want to share the same context with the repositories BUT only for the particular set of requests.

EDIT: ThreadLocal or request based instancing WILL NOT WORK because when I resolve ICompanyService twice from the same thread but used in 2 contexts the thread remains the same and the instance of IDataContext will be the same however they shouldn't be.

I dont see a way to do this where the container is a singleton, the only way i see this is to use the build function to rebuild the whole container on each request and make IDataContext a singleton and everything else transient.

The other problem with this method is I cannot rebuild this container for my more expensive services that I intend to be singletons. Somehow i need to split the container into 2 parts a master containing expensive services or those I dont want to rebuild, and then a derived container that I can use in my build function that will just extend off the master and handle the context specific requests.

Am i just looking at this whole problem all wrong, can someone point me in the right direction?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

烙印 2024-12-20 03:53:23

一种解决方案是提供您自己的 UnitOfWork 模式并实现自定义生活方式来控制数据上下文的生命周期,而不将其绑定到线程、Web 请求等。

您必须在 UnitOfWork 类上使用静态属性来使这项工作,所以您的工作单元可能如下所示:

public class UnitOfWork : IDisposable {
    [ThreadStatic]
    public static UnitOfWork Current { get; set; }

    private IDictionary<string, object> _items;

    public IDictionary<string, object> Items 
    {
         get { 
              if(_items == null) 
              {
                 _items = new Dictionary<string, object>();
              }
              return _items;
         }
    }

    public void Dispose()
    {
         Current = null;
    }

    public static UnitOfWork Start() 
    {
         Current = new UnitOfWork();
         return Current;
    }
}

一旦您有了类似的东西,您就可以像这样使用它:

using(UnitOfWork.Start())
{
    var companyService = container.Resolve<ICompanyService>();
    // .. use company service
}

最后您的自定义生活方式将如下所示:

public class UnitOfWorkLifestyleManager : AbstractLifestyleManager
{
    private string PerUnitOfWorkObjectID = "UnitOfWorkLifestyleManager_" + Guid.NewGuid().ToString(); 

    public override object Resolve()
    {
        if(UnitOfWork.Current.Items[PerUnitOfWorkObjectID] == null)
        {
            // Create the actual object
            UnitOfWork.Current.Items[PerUnitOfWorkObjectID] = base.Resolve();  
        }

        return UnitOfWork.Current.Items[PerUnitOfWorkObjectID];
    }

    public override void Dispose()
    { 
    }
}

并注册您的自定义生活方式:

container.Register(Component.For<SqlContext, IDataContext>().Lifestyle.Custom<UnitOfWorkLifestyle>());

One solution is to provide your own UnitOfWork pattern and implement a custom lifestyle to control the life of the data context, without tying it to a thread, web-request, etc.

You would have to use a static property on your UnitOfWork class to make this work, so your unit of work might look like this:

public class UnitOfWork : IDisposable {
    [ThreadStatic]
    public static UnitOfWork Current { get; set; }

    private IDictionary<string, object> _items;

    public IDictionary<string, object> Items 
    {
         get { 
              if(_items == null) 
              {
                 _items = new Dictionary<string, object>();
              }
              return _items;
         }
    }

    public void Dispose()
    {
         Current = null;
    }

    public static UnitOfWork Start() 
    {
         Current = new UnitOfWork();
         return Current;
    }
}

Once you have something like that in place you could use it like this:

using(UnitOfWork.Start())
{
    var companyService = container.Resolve<ICompanyService>();
    // .. use company service
}

Lastly your custom lifestyle would look like this:

public class UnitOfWorkLifestyleManager : AbstractLifestyleManager
{
    private string PerUnitOfWorkObjectID = "UnitOfWorkLifestyleManager_" + Guid.NewGuid().ToString(); 

    public override object Resolve()
    {
        if(UnitOfWork.Current.Items[PerUnitOfWorkObjectID] == null)
        {
            // Create the actual object
            UnitOfWork.Current.Items[PerUnitOfWorkObjectID] = base.Resolve();  
        }

        return UnitOfWork.Current.Items[PerUnitOfWorkObjectID];
    }

    public override void Dispose()
    { 
    }
}

And registering your custom life style:

container.Register(Component.For<SqlContext, IDataContext>().Lifestyle.Custom<UnitOfWorkLifestyle>());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文