0

现在我的 ThingController 的方法都带有一个标识资源的 id 参数,即 URL 是 /{controller}/{action}/{id}。所以当然每个方法做的第一件事就是 var thing = thingFactory.Get(id)。现在,Thing 是一个实时的、线程安全的运行时对象,可在多个用户和会话之间共享。

所以我想要做的是让 DI 框架使用 id 从自定义范围(“生活方式?”)中获取事物并将其注入到每个请求的控制器中。如果该 id 尚不存在任何事物,它还应该创建一个新事物。

4

2 回答 2

2

如果您尝试将事物注入控制器以删除所有方法中的“var thing = thingFactory.Get(id)”行,则可以使用 ASP.NET MVC 自定义模型绑定器在开始之前执行检索你的方法代码。

如果您的路线看起来像: /{controller}/{action}/{id} ,那么您将让 id 查找您的值。

模型绑定将允许您的方法签名如下所示:

public ActionResult SomeDetails(int id, Thing thing)
{

模型绑定和验证在方法代码执行之前触发。DefaultModelBinder 只会从 FormCollection 或其他非持久源填充您的类。

我推荐的技巧是为执行查找的 Thing 类提供自定义模型绑定器 (IModelBinder)。从 DefaultModelBinder 派生,注入您的 IThingFactory,并覆盖 BindModel() 以按照您的方式进行查找。

public class ThingModelBinder : DefaultModelBinder
{
    IThingFactory ThingFactory;

    public ThingModelBinder(IThingFactory thingFactory)
    {
        this.IThingFactory = thingFactory;
    }

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        Thing thing = new Thing();

        string id_string = controllerContext.RouteData.Values["id"].ToString();
        int id = 0;
        Int32.TryParse(id_string, out id);
        var thing = thingFactory.Get(id);

        return thing;
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }

        // custom section
        object model = this.CreateModel(controllerContext, bindingContext, typeof(Thing));

        // set back to ModelBindingContext
        bindingContext.ModelMetadata.Model = model;

        // call base class version
        // this will use the overridden version of CreateModel() which calls the Repository
        // object model = BindComplexModel(controllerContext, bindingContext);

        return base.BindModel(controllerContext, bindingContext);

    }

然后,您将在 global.asax Application_Start() 中配置模型绑定器,您已经在其中创建了容器:

    // Custom Model Binders
    System.Web.Mvc.ModelBinders.Binders.Add(
        typeof(MyCompany.Thing)
        , new MyMvcApplication.ModelBinders.ThingModelBinder(
            WindsorContainer.Resolve<IThingFactory>()
            )
        );

这将导致您的自定义模型绑定器被调用。

您以正常方式解决 Windsor 依赖项。

这是基于我做的一个帖子

于 2012-04-27T23:21:35.973 回答
1

如果我对你的理解正确,你有一些固定数量Things的容器注册了,你想要一个工厂返回给定 ID 的东西,这样如果你说两次你每次Get(1)都会得到相同的东西,如果你说你会得到不一样的东西。ThingGet(2)

实现这一目标的一种方法是使用Typed Factory Facility,如下所示:

// This is the thing factory - it will create the thing if it has not already 
// been created with the given ID - if it is already created it will return 
// that instance
public interface IThingFactory
{
    Thing Get(int id);
}

// This is the thing - it has an ID and a method that you
// can call that keeps track of how many times it has been
// called (so you can be sure it is the same instance)
public class Thing
{
    private int _count;

    public Thing(int id)
    {
        Id = id;
    }

    public int Id { get; private set; }

    public int HowManyCalls()
    {
        return Interlocked.Increment(ref _count);
    }
}

// This is a typed factory selector to manage selecting the component from
// the container by using the name ("Thing" followed by the ID)
public class GetThingComponentSelector : ITypedFactoryComponentSelector
{
    public TypedFactoryComponent SelectComponent(MethodInfo method, 
                                                 Type type, 
                                                 object[] arguments)
    {
        return new TypedFactoryComponent("Thing" + arguments[0],
                                         typeof(Thing),
                                         new Arguments(arguments));
    }
}

// .... In the installer ....

// Register each thing with a different name that matches the ID
// and register a custom component selector and the thing factory
container
    .AddFacility<TypedFactoryFacility>()
    .Register(
        Component
            .For<Thing>()
            .Named("Thing1"),
        Component
            .For<Thing>()
            .Named("Thing2"),
        Component
            .For<GetThingComponentSelector>(),
        Component
            .For<IThingFactory>()
            .AsFactory(c => c.SelectedWith<GetThingComponentSelector>()));

// ... Some demo code (you do not need to resolve the factory directly)

// Now resolve the same thing twice and then a different thing and make sure
// Windsor has handled the lifestyle
var thing = container.Resolve<IThingFactory>().Get(1);
Console.WriteLine("ID should be 1 and is " + thing.Id 
    + ". Calls should be 1 and is " + thing.HowManyCalls());

thing = container.Resolve<IThingFactory>().Get(1);
Console.WriteLine("ID should be 1 and is " + thing.Id 
    + ". Calls should be 2 and is " + thing.HowManyCalls());

thing = container.Resolve<IThingFactory>().Get(2);
Console.WriteLine("ID should be 2 and is " + thing.Id 
    + ". Calls should be 1 and is " + thing.HowManyCalls());

在那里,您会看到我使用 ID 作为容器中的“名称”,然后使用自定义选择器查找名称。可能还有其他方法可以做到这一点,但我认为,根据你的问题,这至少应该让你开始。

于 2012-04-23T17:48:39.610 回答