Связыватель базовой модели .NET только для базового класса - PullRequest
0 голосов
/ 25 января 2019
public class BondsFilterModel : BaseListRequest
{
    public string Keyword { get; set; }

    public DateTime? DateFrom { get; set; }

    public DateTime? DateTo { get; set; }
}

 public class BaseListRequest
{
    public int PageSize { get; set; }

    public int Page { get; set; }

    public string SortColumn { get; set; }

    public SortDirection SortDirection { get; set; }
}

public enum SortDirection
{
    Ascending = 1,
    Descending = 2
}

В полном .NET Framework вы могли бы сделать

public class BaseListRequestModelBinder : DefaultModelBinder
{
    public const string KendoSort = "sort";
    public const string KendoPage = "page";
    public const string KendoPageSize = "pageSize";
    public const char SplitChar = '-';
    public const string AscendingDirectionString = "asc";

    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;

        if (bindingContext.Model is BaseListRequest model)
        {
            var page = Convert.ToInt32(request.Form[KendoPage]);
            model.Page = Math.Max(page - 1, 0);

            model.PageSize = Convert.ToInt32(request.Form[KendoPageSize]);

            var sort = request.Form[KendoSort];

            if (!string.IsNullOrWhiteSpace(sort))
            {
                model.SortColumn = sort.Remove(sort.LastIndexOf(SplitChar));
                model.SortDirection = sort.Split(SplitChar)
                    .LastOrDefault()
                    ?.Equals(AscendingDirectionString, StringComparison.InvariantCultureIgnoreCase) ?? false ? SortDirection.Ascending : SortDirection.Descending;
            }
        }

        base.OnModelUpdated(controllerContext, bindingContext);
    }
}

public class BaseListRequestModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(Type modelType)
    {
        if (typeof(BaseListRequest).IsAssignableFrom(modelType))
        {
            return new BaseListRequestModelBinder();
        }

        return null;
    }
}

и в global.asax

    protected void Application_Start()
    {
       // other code
        ModelBinderProviders.BinderProviders.Add(new BaseListRequestModelBinderProvider());
    }

и это будет заполнять только свойства базового класса, производный класс будет заполняться из связующего устройства модели по умолчанию.

В .NET Core, когда я делаю:

public class BaseListRequestModelBinder : IModelBinder
{
    public const string KendoSort = "sort";
    public const string KendoPage = "page";
    public const string KendoPageSize = "pageSize";
    public const char SplitChar = '-';
    public const string AscendingDirectionString = "asc";

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var model = Activator.CreateInstance(bindingContext.ModelType) as BondsFilterModel;

        var pageString = bindingContext.ValueProvider.GetValue(KendoPage).FirstValue;

        var page = Convert.ToInt32(pageString);
        model.Page = Math.Max(page - 1, 0);

        var pageSizeString = bindingContext.ValueProvider.GetValue(KendoPageSize).FirstValue;
        model.PageSize = Convert.ToInt32(pageSizeString);

        var sort = bindingContext.ValueProvider.GetValue(KendoSort).FirstValue;

        if (!string.IsNullOrWhiteSpace(sort))
        {
            model.SortColumn = sort.Remove(sort.LastIndexOf(SplitChar));
            model.SortDirection = sort.Split(SplitChar)
                .LastOrDefault()
                ?.Equals(AscendingDirectionString, StringComparison.InvariantCultureIgnoreCase) ?? false ? SortDirection.Ascending : SortDirection.Descending;
        }

        bindingContext.Result = ModelBindingResult.Success(model);

        return Task.CompletedTask;
    }
}

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

        if (typeof(BaseListRequest).IsAssignableFrom(context.Metadata.ModelType))
        {
            return new BinderTypeModelBinder(typeof(BaseListRequestModelBinder));
        }

        return null;
    }
}

и в Startup.cs

services.AddMvc(config => config.ModelBinderProviders.Insert(0, new BaseListRequestModelBinderProvider()))

Мой связыватель вызывается, и базовый класс привязан правильно. Но свойства производного класса не устанавливаются. Я предполагаю, что основное различие между полной .NET Framework и Core состоит в том, что мне приходится создавать экземпляр Model самостоятельно в связующем в Core, тогда как в полной .NET Framework я изменяю существующую модель.

Есть ли способ связать только базовый класс и оставить производный класс для связывателей по умолчанию, как в полной .NET Framework?

1 Ответ

0 голосов
/ 27 января 2019

Коллекция провайдеров связывателей моделей рассматривается по порядку.

В .Net Framework вы добавляли свой провайдер связывателей моделей в конце сбора.Следовательно, он позволит вызывать поставщика связывателя встроенной модели перед привязкой пользовательской модели.

ModelBinderProviders.BinderProviders.Add(new BaseListRequestModelBinderProvider());

Находясь в .Net Core, вы добавляли своего поставщика связывателя пользовательской модели в начале сбора.вставляя его с индексом 0.

services.AddMvc(config => config.ModelBinderProviders.Insert(0, new BaseListRequestModelBinderProvider()));

Таким образом, решение состоит в том, чтобы добавить его в конец коллекции, чтобы позволить встроенному поставщику модели вызываться перед вашим пользовательским, тем же шагом, что и вы.сделано в .Net Framework.

services.AddMvc(config => config.ModelBinderProviders.Add(new BaseListRequestModelBinderProvider()));
...