Entity Framework Core - настройка конвертера значений в общем - PullRequest
0 голосов
/ 24 октября 2018

В настоящее время я тестирую Entity Framework Core 2.1 с целью его использования в компании, в которой я работаю, для бизнес-приложений.У меня большая часть возможностей по внедрению преобразователей значений в моем тестовом проекте, но моя существующая база знаний подвела меня к последнему препятствию!

Что я пытаюсь сделать

Мойпонимание состоит в том, что для значений перечисления встроенные преобразователи типов могут преобразовывать из значения перечисления в строковый эквивалент (EnumToStringConverter) или из значения перечисления в его числовое представление (EnumToNumberConverter).Однако мы используем пользовательское строковое значение для представления перечисления в нашей базе данных, поэтому я написал собственный EnumToDbStringEquivalentConvertor для этого преобразования, и строковое значение базы данных указано в качестве атрибута для каждого из значений перечисления в моей модели.

Код выглядит следующим образом:

Модель

public class User
{
    [Key] public int ID { get; set; }
    public EmployeeType EmployeeType { get; set; }
}

public enum EmployeeType
{
    [EnumDbStringValue("D")]
    Director,
    [EnumDbStringValue("W")]
    Weekly,
    [EnumDbStringValue("S")]
    Salaried
}

DataContext

public class MyDataContext : DbContext
{
    public DbSet<User> Users { get; set; }

    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType.IsEnum)
                {
                    property.SetValueConverter(new EnumToDbStringEquivalentConvertor<EmployeeType>());
                }
             }
         }
    }
}

ЗначениеКонвертер

public class EnumToDbStringEquivalentConvertor<T> : ValueConverter<T, string>
{
    public EnumToDbStringEquivalentConvertor(ConverterMappingHints mappingHints = null) : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
    { }

    private static Expression<Func<T, string>> convertToProviderExpression = x => ToDbString(x);
    private static Expression<Func<string, T>> convertFromProviderExpression = x => ToEnum<T>(x);

    public static string ToDbString<TEnum>(TEnum tEnum)
    {
        var enumType = tEnum.GetType();
        var enumTypeMemberInfo = enumType.GetMember(tEnum.ToString());
        EnumDbStringValueAttribute enumDbStringValueAttribute = (EnumDbStringValueAttribute)enumTypeMemberInfo[0]
            .GetCustomAttributes(typeof(EnumDbStringValueAttribute), false)
            .FirstOrDefault();

        return enumDbStringValueAttribute.StringValue;
    }

    public static TEnum ToEnum<TEnum>(string stringValue)
    {
        // Code not included for brevity
    }
}

Этот код (я рад сообщить), кажется, работает без проблем.

Моя проблема

Документация по преобразователям значенийкажется, предлагает способ, которым мы назначаем их в методе OnModelCreating, чтобы физически назначить каждый отдельный преобразователь типа каждому отдельному свойству в модели.Я не хочу этого делать - я хочу, чтобы моя модель была водителем.Я реализую это позже, но пока, в текущей версии кода, я перебираю типы сущностей в моей модели, проверяя значение свойства IsEnum и затем назначая преобразователь значения в этой точке.

Моя проблема в том, что метод расширения SetValueConverter, который я использую, требует от меня передать ему новый экземпляр EnumToDbStringEquivalentConvertor, который в моем примере жестко запрограммирован как EnumToDbStringEquivalentConvertor, который работает.Однако я не хочу, чтобы это было жестко закодировано - я хочу передать тип сущности ClrType.

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

Это:

public class MyDataContext : DbContext
{
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType.IsEnum)
                {
                    var converterType = typeof(EnumToDbStringEquivalentConvertor<>);
                    var genericConverterType = converterType.MakeGenericType(property.ClrType);

                    MethodInfo setValueConverterMethodInfo = typeof(MutablePropertyExtensions).GetMethod("SetValueConverter");
                    setValueConverterMethodInfo.Invoke(property,
                            new object[] { property, Activator.CreateInstance(genericConverterType) });
                }
             }
         }
    }
}

выдает мне сообщение об ошибке « System.MissingMethodException :« Не определен конструктор без параметров для этого объекта. »наМетод GetModel в Microsoft.EntityFrameworkCore.Infrastructure

Так что мой вопрос: может ли кто-нибудь посоветовать мне, как я могу передать свой преобразователь значений в общем случае в метод SetValueConveter EF Core?

Заранее спасибо заваша помощь.

1 Ответ

0 голосов
/ 24 октября 2018

Вы почти у цели.Проблема заключается в следующем коде

Activator.CreateInstance(genericConverterType)

, который пытается найти и вызвать конструктор без параметров вашего класса преобразователя.Но ваш конструктор класса имеет параметр , хотя и необязательный.Параметры Pptional являются просто сахаром компилятора, при использовании отражения вы должны передавать их явно.

Поэтому вам нужно использовать перегрузку CreateInstance , принимающую params object[] args и передающую null для mappingHints.

Также нет необходимости вызывать SetValueConverter через отражение - это часть общедоступного API.

Рабочий код может выглядеть следующим образом:

if (property.ClrType.IsEnum)
{
    var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
        .MakeGenericType(property.ClrType);    
    var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
    property.SetValueConverter(converter);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...