简体   繁体   English

具有IoC容器并发症的C#ASP.NET依赖注入

[英]C# ASP.NET Dependency Injection with IoC Container Complications

I apologise for the length, and I know there are some answers on this but I searched a lot and haven't found the right solution, so please bear with me. 很抱歉,我对此表示歉意,但我知道有一些答案,但是我进行了很多搜索,但没有找到正确的解决方案,因此请耐心等待。

I am trying to create a framework for legacy applications to use DI in ASP.NET webforms. 我正在尝试为遗留应用程序创建一个框架,以在ASP.NET Web表单中使用DI。 I will probably use Castle Windsor as the framework. 我可能会使用温莎城堡作为框架。

These legacy applications will use in part an MVP pattern in some places. 这些遗留应用程序将在某些地方部分使用MVP模式。

A presenter would look something like this: 演示者将如下所示:

class Presenter1
{
    public Presenter1(IView1 view, 
        IRepository<User> userRepository)
    {
    }
}

Now the ASP.NET Page would look something like this: 现在,ASP.NET页面将如下所示:

public partial class MyPage1 : System.Web.UI.Page, IView1
{
    private Presenter1 _presenter;
}

Before using DI I would instantiate the Presenter as follows in the OnInit of the page: 在使用DI之前,我将在页面的OnInit中实例化Presenter:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    _presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}

So now I want to use DI. 所以现在我要使用DI。

First I must create a handler factory to override the construction of my page. 首先,我必须创建一个处理程序工厂以覆盖页面的构造。 I found THIS really good answer to help: How to use Dependency Injection with ASP.NET Web Forms 我发现这确实是一个很好的答案,可以帮助您: 如何在ASP.NET Web窗体中使用依赖注入

Now I can easily set up my containers in my composition root as Mark Seeman suggest to use the Global.asax (This means though to create a static container that must be thread safe and sealed not to be able to add further registrations) 现在,正如Mark Seeman建议使用Global.asax一样,我可以在我的合成根目录中轻松地设置容器(这意味着尽管创建了一个静态容器,该容器必须是线程安全的并且密封的,才能添加更多的注册信息)

Now I can go and declare the constructor injection on the page 现在我可以在页面上声明构造函数注入

public MyPage1() : base()
{
}

public MyPage1(Presenter1 presenter) : this()
{
    this._presenter =  presenter;
}

Now we run into the first problem, I have a circular dependency. 现在我们遇到第一个问题,我有一个循环依赖性。 Presenter1 depends on IView1, But the page depends on the presenter. Presenter1取决于IView1,但是页面取决于Presenter。

I know what some will say now that the design is probably incorrect when you have circular dependencies. 我知道现在有些人会说,当您具有循环依赖项时,该设计可能不正确。 First I dont think the Presenter design is incorrect, by it taking a dependency in the constructor to the View, and I can say this by looking at plenty of MVP implementations. 首先,我不认为Presenter设计是不正确的,因为它将构造函数中的依赖关系带给了View,我可以通过查看大量MVP实现来说明这一点。

Some may suggest changing the Page to a design where Presenter1 becomes a property and then using Property injection 有些人可能建议将Page更改为Presenter1成为属性的设计,然后使用属性注入

public partial class MyPage1 : System.Web.UI.Page, IView1
{
    [Dependency]
    public Presenter1 Presenter
    {
        get; set;
    }
}

Some may even suggest removing the dependency to presenter completely and then simply Wiring up via a bunch of events, But this is not the design I wanted and frankly dont see why I need to make this change to accomodate it. 有些人甚至建议完全删除对演示者的依赖,然后通过一系列事件简单地进行布线。但这不是我想要的设计,坦白地说,我不明白为什么我需要进行此更改以适应它。

Anyway regardless of the suggestion, another problem exists: 无论如何建议,仍然存在另一个问题:

When the Handler factory gets a page request only a type is available (NOT THE VIEW INTERFACE): 当处理程序工厂获得页面请求时,只有一种类型可用(没有视图接口):

Type pageType = page.GetType().BaseType;

now using this type you can resolve the Page via IoC and its dependencies: 现在使用此类型,您可以通过IoC及其依赖项来解析Page:

container.Resolve(pageType)

This will then know that there is a property called Presenter1 and be able to inject it. 然后,它将知道存在一个名为Presenter1的属性并能够注入它。 But Presenter1 needs IView1, but we never resolved IView1 through the container, so the container won't know to provide the concrete instance the handler factory just created as it was created outside of container. 但是Presenter1需要IView1,但我们从未通过容器解析IView1,因此容器将不知道提供处理程序工厂刚创建的具体实例,因为它是在容器外部创建的。

So we need to hack our handler factory and replace the view interface: So where the handler factory resolves the page: 因此,我们需要修改处理程序工厂并替换视图接口:处理程序工厂解析页面的位置:

private  void InjectDependencies(object page)
{
    Type pageType = page.GetType().BaseType;
    // hack
    foreach (var intf in pageType.GetInterfaces())
    {
        if (typeof(IView).IsAssignableFrom(intf))
        {
            _container.Bind(intf, () => page); 
        }
    }

    // injectDependencies to page...    
} 

This poses another problem, most containers like Castle Windsor will not allow you to reregister this interface to the instance it is pointing to now. 这带来了另一个问题,大多数城堡(如Castle Windsor)将不允许您将此接口重新注册到它现在指向的实例。 Also with the container being registered in the Global.asax, it is not thread-safe to do as the container should be read only at this point. 同样,在Global.asax中注册了容器的情况下,这样做也不是线程安全的,因为此时只能读取该容器。

The other solution is to create a function to rebuild the container on each web request, and then check to see if the container contains the component IView if not set the instance. 另一个解决方案是创建一个函数,以在每个Web请求上重建容器,然后检查容器(如果未设置实例)是否包含组件IView。 But this seems wasteful and goes against suggested use. 但这似乎很浪费,并且与建议的用法背道而驰。

The other solution is to create a special Factory called IPresenterFactory and put the dependency in the page constructor: 另一种解决方案是创建一个名为IPresenterFactory的特殊工厂,并将依赖项放入页面构造函数中:

public MyPage1(IPresenter1Factory factory) : this()
{
    this._presenter = factory.Create(this);
}

The problem is that you now need to create a factory for each presenter and then make a call to the container to resolve other dependencies: 问题是您现在需要为每个演示者创建一个工厂,然后调用该容器以解决其他依赖项:

class Presenter1Factory : IPresenter1Factory 
{
    public Presenter1Factory(Container container)
    {
        this._container = container;
    }
    public Presenter1 Create(IView1 view)
    {
        return new Presenter1(view, _container.Resolve<IUserRepository>,...)
    }
}

This design also seems cumbersome and over complicated, does any one have ideas for a more elegant solution? 这种设计似乎也很繁琐,是否有人对更优雅的解决方案有想法?

Perhaps I misunderstand your problems, but the solution seems fairly simple to me: promote the IView to a property on the Presenter1 : 也许我误解了您的问题,但是解决方案对我来说似乎很简单:将IView提升为Presenter1上的一个属性:

class Presenter1
{
    public Presenter1(IRepository<User> userRepository)
    {
    }

    public IView1 View { get; set; }            
}

This way you can set the presenter on the view like this: 这样,您可以像这样在视图上设置演示者:

public Presenter1 Presenter { get; set; }

public MyPage1() 
{
    ObjectFactory.BuildUp(this);
    this.Presenter.View = this;
}

Or without property injection, you can do it as follows: 或不使用属性注入,可以按照以下步骤进行操作:

private Presenter1 _presenter;

public MyPage1() 
{
    this._presenter = ObjectFactory.Resolve<Presenter1>();
    this._presenter.View = this;
}

Constructor injection in Page classes and user controls will never really work. Page类和用户控件中的构造方法注入将永远无法真正起作用。 You can get it to work in full trust ( as this article shows ), but it will fail in partial trust. 您可以使它在完全信任下工作( 如本文所示 ),但是在部分信任下它将失败。 So you will have to call the container for this. 因此,您必须为此调用容器。

All DI containers are thread-safe, as long as you don't manually add registrations yourself after the initialization phase and with some containers even that is thread-safe ( some containers even forbid registering types after initialization). 所有DI容器都是线程安全的,只要您没有在初始化阶段之后自己手动添加注册,甚至不使用线程安全的一些容器某些容器甚至在初始化后禁止注册类型)。 There would never be a need to do this (except for unregistered type resolution, which most containers support). 永远不需要这样做(大多数容器支持的未注册类型解析除外)。 With Castle however, you need to register all concrete types upfront, which means it needs to know about your Presenter1 , before you resolve it. 但是,对于Castle,您需要预先注册所有具体类型,这意味着在解决它之前,它需要了解Presenter1 Either register this, change this behavior, or move to a container that allows resolving concrete types by default. 注册,更改此行为或移至默认情况下允许解析具体类型的容器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我应该在ASP.Net应用程序中使用IoC Container(Autofac,Ninject,Unity等)进行依赖注入? - Why should I use IoC Container (Autofac, Ninject, Unity etc) for Dependency Injection in ASP.Net Applications? Asp.Net MVC 5测试IoC +依赖注入 - Asp.Net MVC 5 Testing IoC + Dependency Injection C# Asp.net核心依赖注入使用RestSharp - C# Asp.net core Dependency injection using RestSharp 依赖注入 asp.net n-tier c# - Dependency injection asp.net n-tier c# 使用 ASP.NET MVC 将依赖注入到 C# 中的 model - Dependency injection into model in C# using ASP.NET MVC C#,MVC,IoC,依赖注入 - C#, MVC, IoC, Dependency Injection ASP.NET Web API中的Castle Windsor / DelegatingHandler / IPrincipal依赖注入(DI)/控制反转(IoC) - Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API 具有Ninject依赖注入的ASP.NET MVC应用程序中的MongoDB官方C#驱动程序 - MongoDB Official C# Driver in ASP.NET MVC app with Ninject dependency injection C# ASP.NET Core [Inject] 依赖注入的属性用法 - C# ASP.NET Core [Inject] Attribute Usage for Dependency Injection C# / ASP.NET MVC 依赖注入,数据存储库与 web 隔离 - C# / ASP.NET MVC dependency injection with data repository isolated from web
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM