当关键类需要 Session(或其他上下文特定变量)时如何设置 IoC

发布于 2024-08-14 08:04:54 字数 969 浏览 7 评论 0原文

我试图弄清楚如何在依赖类可以根据应用程序中的某些变量(在本例中为会话状态)更改的情况下使用 IoC。例如,我们的每个客户端都有不同的数据库,因此与数据库的连接需要建立在其会话中存储的值上(特别是因为某些用户如果拥有多个业务,则可能拥有多个数据库,并且会在数据库之间切换) 。

以下是我们当前如何设置此结构的通用示例:

public class MyTestController : ControllerBase
{
    Repository _rep;

    public MyTest(Repository rep)
    {
        _rep = rep;
    }

    public MyTest()
    {
        string connString = String.Format("Server={0}; Database={1};"
            , SessionContainer.ServerName, SessionContainer.DatabaseName;
        var dc = new DataContext(connString);
        _rep = new Repository(dc);
    }

    public int SampleFn()
    {
        return _rep.GetCountOfEmployees();
    }
}

public class Repository
{
    DataContext _context;

    public Repository(DataContext context)
    {
        _context = context;
    }
} 

我们是否能够使用 IoC 进行设置并消除默认的 c-tors?如果是这样,怎么办?我只使用像这样的 DI 没有问题,但我想探索 StructureMap 或 Unity 的可能性(注意:我们通常将 db/server 传递给构建数据上下文的工厂类......上面的示例只是为了简洁)。

I am trying to figure out how to use IoC in situations where the dependent classes can change based on some variable in the application (in this case, Session state). For example, each of our clients have a different database, so the connection to the database needs to be built on a value stored in their Session (particularly since some users could have multiple databases if they own multiple businesses, and would switch between databases).

Here is a generic example of how we'd currently set up this structure:

public class MyTestController : ControllerBase
{
    Repository _rep;

    public MyTest(Repository rep)
    {
        _rep = rep;
    }

    public MyTest()
    {
        string connString = String.Format("Server={0}; Database={1};"
            , SessionContainer.ServerName, SessionContainer.DatabaseName;
        var dc = new DataContext(connString);
        _rep = new Repository(dc);
    }

    public int SampleFn()
    {
        return _rep.GetCountOfEmployees();
    }
}

public class Repository
{
    DataContext _context;

    public Repository(DataContext context)
    {
        _context = context;
    }
} 

Would we be able to set this up using IoC and eliminate the default c-tors? If so, how? I don't have a problem just using D.I. like this, but I'd like to explore the possibility of a StructureMap or Unity (note: we normally pass in db/server to a factory class that builds the datacontext ... above example is just for brevity).

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

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

发布评论

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

评论(1

明媚殇 2024-08-21 08:04:54

存储库实例的创建方式及其生命周期与控制器无关。

当你在容器中注册组件时,你应该指定组件的生命周期。根据您的实现,您可以简单地选择设置存储库的生命周期以跟随会话。

在任何情况下,您都可以使用工厂从会话创建存储库,但要从控制器外部执行此操作。

您肯定需要摆脱默认构造函数。


我一时记不起如何在 Unity 或 StructureMap 中执行此操作,所以这里有一个温莎城堡的示例。

定义一个抽象工厂:

public interface IRepositoryFactory
{
    Repository Create();
}

和一个实现

public class MyRepositoryFactory : IRepositoryFactory
{
    private readonly HttpContextBase httpContext;

    public MyRepositoryFactory(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        this.httpContext = httpContext;
    }

    #region IRepositoryFactory Members

    public Repository Create()
    {
        // return Repository created from this.httpContext
    }

    #endregion
}

现在注册所有内容

container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IRepositoryFactory>()
    .ImplementedBy<MyRepositoryFactory>()
    .LifeStyle.PerWebRequest);
container.Register(Component.For<Repository>()
    .UsingFactory((IRepositoryFactory f) => f.Create())
    .LifeStyle.PerWebRequest);

这里我使用了 PerWebRequest 生活方式,但如果您想优化,您可能需要创建自定义 PerWebSession 生活方式。这在 Castle 中并不太难,但我不记得在其他 DI 容器中有多难。

您还需要注册 HttpContextBase,因为 MyRepositoryFactory 依赖于它。

How the Repository instance is created, as well as its lifetime, is of no concern of the Controller.

When you register components in the container, you should specify the lifetime of the component. Depending on your implementation, you may simply choose to set the lifefime of the Repository to follow the session.

In any case you could use a factory to create the repository from the session, but do this from outside the Controller.

You definitely need to get rid of the default constructor.


Off the top of my head I can't remember how to do this in Unity or StructureMap, so here's a Castle Windsor example.

Define an Abstract Factory:

public interface IRepositoryFactory
{
    Repository Create();
}

and an implementation

public class MyRepositoryFactory : IRepositoryFactory
{
    private readonly HttpContextBase httpContext;

    public MyRepositoryFactory(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        this.httpContext = httpContext;
    }

    #region IRepositoryFactory Members

    public Repository Create()
    {
        // return Repository created from this.httpContext
    }

    #endregion
}

Now register all the stuff

container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IRepositoryFactory>()
    .ImplementedBy<MyRepositoryFactory>()
    .LifeStyle.PerWebRequest);
container.Register(Component.For<Repository>()
    .UsingFactory((IRepositoryFactory f) => f.Create())
    .LifeStyle.PerWebRequest);

Here I've used the PerWebRequest lifestyle, but if you want to optimize you might want to create a custom PerWebSession lifestyle. This is not too hard to do in Castle, but I can't remember how hard it is in other DI Containers.

You will also need to register HttpContextBase, since MyRepositoryFactory depends on it.

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