Связывание пользовательской модели с использованием отражения в ASP. NET Core - PullRequest
0 голосов
/ 14 июля 2020

У меня есть следующая модель

public class MyModel
{
   public string Name {get;set;}
   public int? Age {get;set;}
   public string City {get;set;}
   public decimal? Salary {get;set;}
   public JObject ExtraFields {get;set;}
}

Я пытаюсь реализовать Custom Model Binder. Если отправленная форма имеет key, что соответствует свойствам модели, тогда установите значение свойства модели, иначе добавьте ключ и значение к ExtraFields. Обратите внимание, что ExtraFields - это JObject

public class MyModelBinder: IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }


        MyModel model = new MyModel()
        {
            ExtraFields = new JObject()
        };

        var form = bindingContext.HttpContext.Request.Form;

        var properties = typeof(MyModel).GetProperties();
        foreach (var key in form.Keys)
        {
            var p = properties.FirstOrDefault(x => x.Name == key);
            var val = form[key];
            if (p != null)
            {
                p.SetValue(model, val); // throws exception
            }
            else
            {
                var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
                model.ExtraFields.Add(key, v);
            }
        }

        bindingContext.Model = model;
        bindingContext.Result = ModelBindingResult.Success(model);

        return Task.CompletedTask;
    }
}

Issue Я получаю исключение при установке значения модели

Object of type 'Microsoft.Extensions.Primitives.StringValues' cannot be converted to type 'System.String

Если возможно, я хотел бы избежать проверки типа целевого свойства модели, а затем преобразовать значение в целевой тип. Это должно происходить неявно.

В основном, для всех совпадающих ключей вызывается связка по умолчанию ASP. NET, а для всех оставшихся ключей добавляется значение ExtraFields

1 Ответ

1 голос
/ 15 июля 2020

Объект типа 'Microsoft.Extensions.Primitives.StringValues' не может быть преобразован в тип 'System.String

val имеет тип Microsoft.Extensions.Primitives.StringValues ​​, который не может напрямую назначать его значение полям модели.

Во-первых, вы можете получить строковую форму соответствующего значение напрямую через val.ToString().

Так как тип каждого поля отличается, вы можете создать собственный метод Convert.ChangeType в динамически преобразовать тип, передав val.ToString() и p.PropertyType.

Подробнее см. В этой демонстрации:

  public class MyModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }
 
            MyModel model = new MyModel()
            {
                ExtraFields = new JObject()
            };

            var form = bindingContext.HttpContext.Request.Form;

            var properties = typeof(MyModel).GetProperties();
            foreach (var key in form.Keys)
            {
                var p = properties.FirstOrDefault(x => x.Name == key);
                var val = form[key];
                if (p != null)
                {
                   // call custom method ChangeType and pass two parameters.
                    p.SetValue(model, ChangeType(val.ToString(),p.PropertyType)); 

                }
                else
                {
                    var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
                    model.ExtraFields.Add(key, v);
                }
            }

            bindingContext.Model = model;
            bindingContext.Result = ModelBindingResult.Success(model);

            return Task.CompletedTask;
        }

        // custom method
        public static object ChangeType(object value, Type conversion)
        {
            var t = conversion;

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return null;
                }

                t = Nullable.GetUnderlyingType(t);
            }

            return Convert.ChangeType(value, t);
        }
    }

Обновление

@ LP13 обеспечивает более простое решение :

TypeConverter typeConverter = TypeDescriptor.GetConverter(p.PropertyType); 
object propValue = typeConverter.ConvertFromString(val); p.PropertyType));  
p.SetValue(model, propValue); 

Вот мой результат теста:

введите описание изображения здесь

...