Asp Net Core RC2. Связывание абстрактной модели класса

В RC1 я использую следующий код для привязки абстрактных классов или интерфейсов:

public class MessageModelBinder : IModelBinder {

    public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) {
        if(bindingContext.ModelType == typeof(ICommand)) {
            var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
            if(msgTypeResult == ValueProviderResult.None) {
                return ModelBindingResult.FailedAsync(bindingContext.ModelName);
            }
            var type = Assembly.GetAssembly(typeof(MessageModelBinder )).GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
            if(type == null) {
                return ModelBindingResult.FailedAsync(bindingContext.ModelName);
            }
            var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
            bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
        }
        return ModelBindingResult.NoResultAsync;
    }
}

Это связующее читает только тип модели (messageType параметр) из строки запроса и переопределяет тип метаданных. А остальная часть работы выполняется стандартными связующими, такими как BodyModelBinder,

В Startup.cs я просто добавляю первый компоновщик:

services.AddMvc().Services.Configure<MvcOptions>(options => {
    options.ModelBinders.Insert(0, new MessageModelBinder());
});

контроллер:

[Route("api/[controller]")]
public class MessageController : Controller {
    [HttpPost("{messageType}")]
    public ActionResult Post(string messageType, [FromBody]ICommand message) {
    } 
}

Как я могу выполнить это в RC2?

Насколько я понимаю, теперь я должен использовать IModelBinderProvider, Хорошо, я попробовал Startup.cs:

services.AddMvc().Services.Configure<MvcOptions>(options => {
    options.ModelBinderProviders.Insert(0, new MessageModelBinderProvider());
});

ModelBinderProvider:

public class MessageModelBinderProvider : IModelBinderProvider {
    public IModelBinder GetBinder(ModelBinderProviderContext context) {
        if(context == null) {
            throw new ArgumentNullException(nameof(context));
        }
        return context.Metadata.ModelType == typeof(ICommand) ? new MessageModelBinder() : null;
    }
}

ModelBinder:

public class MessageModelBinder : IModelBinder {
    public Task BindModelAsync(ModelBindingContext bindingContext) {
        if(bindingContext.ModelType == typeof(ICommand)) {
            var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
            if(msgTypeResult == ValueProviderResult.None) {
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                return Task.FromResult(0);
            }
            var type = typeof(MessageModelBinder).GetTypeInfo().Assembly.GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
            if(type == null) {
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                return Task.FromResult(0);
            }
            var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
            bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
            bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, Activator.CreateInstance(type));
        }
        return Task.FromResult(0);
    }
}

Но я не могу указать NoResult, Если я не укажу bindingContext.ResultЯ получаю нулевую модель в контроллере. Если я укажу bindingContext.Result, Я получаю пустую модель без установки полей модели.

1 ответ

У меня было похожее требование с пользовательскими моделями привязки и абстрактными классами, и предложения, опубликованные dougbu на github AspNet / Mvc / questions / 4703, работали для меня. Я обновил RC1 до ASP.NET Core 1.0, и мне нужно было модифицировать привязку моей пользовательской модели с его рекомендациями. Я скопировал и вставил его код ниже на случай, если разорвется ссылка на проблему с github. Прочитайте комментарии в выпуске github, чтобы узнать о безопасности кода, который создает на сервере объекты запрошенного типа.

MessageModelBinderProvider

public class MessageModelBinderProvider : IModelBinderProvider 
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType != typeof(ICommand))
        {
            return null;
        }

        var binders = new Dictionary<string, IModelBinder>();
        foreach (var type in typeof(MessageModelBinderProvider).GetTypeInfo().Assembly.GetTypes())
        {
            var typeInfo = type.GetTypeInfo();
            if (typeInfo.IsAbstract || typeInfo.IsNested)
            {
                continue;
            }

            if (!(typeInfo.IsClass && typeInfo.IsPublic))
            {
                continue;
            }

            if (!typeof(ICommand).IsAssignableFrom(type))
            {
                continue;
            }

            var metadata = context.MetadataProvider.GetMetadataForType(type);
            var binder = context.CreateBinder(metadata);
            binders.Add(type.FullName, binder);
        }

        return new MessageModelBinder(context.MetadataProvider, binders);
    }
}

MessageModelBinder

public class MessageModelBinder : IModelBinder
{
    private readonly IModelMetadataProvider _metadataProvider;
    private readonly Dictionary<string, IModelBinder> _binders;

    public MessageModelBinder(IModelMetadataProvider metadataProvider, Dictionary<string, IModelBinder> binders)
    {
        _metadataProvider = metadataProvider;
        _binders = binders;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var messageTypeModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "messageType");
        var messageTypeResult = bindingContext.ValueProvider.GetValue(messageTypeModelName);
        if (messageTypeResult == ValueProviderResult.None)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        IModelBinder binder;
        if (!_binders.TryGetValue(messageTypeResult.FirstValue, out binder))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // Now know the type exists in the assembly.
        var type = Type.GetType(messageTypeResult.FirstValue);
        var metadata = _metadataProvider.GetMetadataForType(type);

        ModelBindingResult result;
        using (bindingContext.EnterNestedScope(metadata, bindingContext.FieldName, bindingContext.ModelName, model: null))
        {
            await binder.BindModelAsync(bindingContext);
            result = bindingContext.Result;
        }

        bindingContext.Result = result;
    }
}
Другие вопросы по тегам