首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模型标识接口的ModelBinder

模型标识接口的ModelBinder
EN

Stack Overflow用户
提问于 2010-12-26 03:13:12
回答 3查看 932关注 0票数 2

我有一个IIdentifiable接口:

代码语言:javascript
复制
public interface IIdentifiable
{
    int Id { get; set; }
}

和一个简单的Foo

代码语言:javascript
复制
public class Foo : IIdentifiable
{
    public int Id { get; set; }
    public string Name { get; set; }
}

当我有一个页面需要添加一组Foo并指定一个作为缺省值时,我将有一个如下所示的视图模型:

代码语言:javascript
复制
public class BarViewModel
{
    public IList<Foo> Foos { get; set; }
    public Foo DefaultFoo { get; set; }
}

因此,在现实中,我只想传递‘d,而不是在隐藏输入中传递实际的完整对象(这是讨厌的,不需要的)。所有bar所关心的(至少在数据库中)都在一个Bar和它的Foo.Ids和默认的Foo.Id之间。

我希望能够轻松地添加一个能够接受所有IIdentifiable的模型绑定器,然后如果只为值提供程序设置一个int,则设置Id。我遇到的问题是,我无法执行如下操作并让它设置Id (因为模型绑定器不查看派生类型chain...ugh):

代码语言:javascript
复制
ModelBinders.Binders[typeof(IIdentifiable)] = new IdentifiableModelBinder();

因此,我决定扩展DefaultModelProvider以允许这种功能,因为如果类型是IIdentifiable,而在值提供程序中找到的值只是一个字符串/int,那么创建模型并将Id属性设置为匹配的值:

代码语言:javascript
复制
public class DefaultWithIdentifiableModelBinder : DefaultModelBinder
{

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

        var modelType = bindingContext.ModelType;
        bool isList = false;

        // Determine the real type of the array or another generic type.
        if (modelType.IsArray)
        {
            modelType = modelType.GetElementType();
            isList = true;
        }
        else if (modelType.IsGenericType)
        {
            var genericType = modelType.GetGenericTypeDefinition();

            if (genericType == typeof(IEnumerable<>) || genericType == typeof(IList<>) || genericType == typeof(ICollection<>))
            {
                modelType = modelType.GetGenericArguments()[0];
                isList = true;
            }
        }

        // The real model type isn't identifiable so use the default binder.
        if (!typeof(IIdentifiable).IsAssignableFrom(modelType))
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        // Get the value provider for the model name.
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        // Get the string values from the value provider.
        var stringValues = valueProviderResult != null ? (IEnumerable<string>)valueProviderResult.RawValue : Enumerable.Empty<string>();

        int tempFirstId = -1;

        // If the first element is an integer, we assume that only the Ids are supplied
        // and therefore we parse the list.
        // Otherwise, use the default binder.
        if (stringValues.Any() && int.TryParse(stringValues.First(), out tempFirstId))
        {

            var listType = typeof(List<>).MakeGenericType(new Type[] { modelType });

            var items = (IList)base.CreateModel(controllerContext, bindingContext, listType);

            // Create each identifiable object and set the Id.
            foreach (var id in stringValues)
            {
                var item = (IIdentifiable)Activator.CreateInstance(modelType);
                item.Id = int.Parse(id);
                items.Add(item);
            }

            if (items.Count == 0)
            {
                return null;
            }

            // Determine the correct result to return.
            if (bindingContext.ModelType.IsArray)
            {
                var array = Array.CreateInstance(modelType, items.Count);
                items.CopyTo(array, 0);
                return array;
            }
            else if (isList)
            {
                return items;
            }
            else
            {
                return items[0];
            }
        }
        else
        {
            return base.BindModel(controllerContext, bindingContext);
        }

    }

}

我只是不确定这是否真的是我想要做的事。如果有人能在这种类型的模型绑定上留下反馈/建议/改进,我们将不胜感激。

编辑:下面是一个简单的示例:

订单有许多项,因此,与其加载项目的整个对象图,实际上只需要Id才能用于ORM和数据库中。因此,item类使用Id加载,然后可以将该项添加到订单的项目列表中:

代码语言:javascript
复制
public class Order
{
    List<Item> Items { get; set; }
}

var order = new Order();
order.Items.Add(new Item() { Id=2 });
order.Items.Add(new Item() { Id=5 });

这是因为完成订单的回发不是发送整个Item,而是发送it。

这是我的核心要求。当回发发生时,我需要从回发的Id构建项目。不管这是一个视图模型还是实际的域模型,我仍然需要一种从ints到Id设置的域模型的转换方法。讲得通?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-12-26 04:36:33

这似乎是一种不错的方式,但并不是很容易扩展。如果您不太可能在不同的接口中出现相同的情况,那么这是一个很好的解决方案。

另一种方法是在启动时使用反射查找实现IIdentifiable的所有类型,并为所有这些类型分配自定义模型绑定。

票数 1
EN

Stack Overflow用户

发布于 2019-04-26 13:44:05

我经历过类似的问题,如下面的解决方案。

首先,创建您的界面。在下面的示例中,它的名称是IInterface。

在此之后,创建您的CustomBinderProvider如下;

代码语言:javascript
复制
public class IInterfaceBinderProvider : IModelBinderProvider
{
       public IModelBinder GetBinder(ModelBinderProviderContext context)
       {
           if (context.Metadata.ModelType.GetInterface(nameof(IInterface)) != null)
           {
               return new BinderTypeModelBinder(typeof(IInterface));
           }

           return null;
       }
}

现在,您可以按以下方式实现您的绑定器;

代码语言:javascript
复制
public Task BindModelAsync(ModelBindingContext bindingContext)
{
           // Do something

           var model = (IInterface)Activator.CreateInstance(bindingContext.ModelType);

           // Do something

           bindingContext.Model = model;
           bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
           return Task.FromResult(model);
} 

最后,您必须将提供程序插入到启动类,如下所示;

代码语言:javascript
复制
services.AddMvcCore(x =>
{
     x.ModelBinderProviders.Insert(0, new IInterfaceBinderProvider());
})
票数 3
EN

Stack Overflow用户

发布于 2010-12-26 07:39:29

那么,假设您需要Foo及其数据。因此,您为它保存Name (可能与其他属性一起),并将持久性中的所有数据作为Foo实例检索,这正是,也就是您的域实体用于的内容。它包含与Foo相关的所有数据。

另一方面,您有一个视图,您只想使用Id来操作,所以您可以创建包含Id作为属性的ViewModel (例如,可能还有更多的Ids,比如ParentFooId ),这就是,您的ViewModel for。它只包含特定于您的视图的数据-它类似于您的视图和控制器之间的接口。

这样,所有的事情都是用DefaultModelBinder完成的。例如,如果您有:

在您的form;

  • BarViewModel字典中使用的id (类型为int)参数,或者通过BarViewModel

实例将其作为控制器的BarViewModel的Action;

  • Id属性的参数发布

然后,根据请求,该barViewModel.Id属性的值将是来自RouteData (或表单)的值,因为DefaultModelBinder能够做到这一点。而且您只为非常不寻常的场景创建自定义IModelBinder

我看不出有什么理由让事情变得过于复杂。

合乎道理?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4532598

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档