Проверка, реализует ли класс интерфейс Generi c - PullRequest
2 голосов
/ 19 марта 2020

Я использую следующую обобщенную функцию c, чтобы определить, реализует ли класс указанный интерфейс:

private static bool HasFieldType<TEntity, TInterface>()
{
    return typeof(TInterface).IsAssignableFrom(typeof(TEntity));
}

В большинстве случаев это работает нормально.

Однако теперь у меня есть интерфейс, который имеет универсальный параметр c:

public interface IStatusField<TEnum> where TEnum : System.Enum
{
    TEnum Status { get; set; }
}

И это приводит к сбою функции HasFieldType с ошибкой unexpected use of unbound generic name.

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

if (HasFieldType<TEntity, IStatusField<>>()) 
{
    // builder is an EntityTypeBuilder instance
    builder.Property("Status")
        .HasMaxLength(255)
        .HasConversion(new EnumToStringConverter<>());
}

Но это не сработает, поскольку я не указываю тип generi c для IStatusField<> или EnumToStringConverter<>.

Есть ли способ обойти это?

ОБНОВЛЕНИЕ

Этот код является частью обобщенного c base IEntityTypeConfiguration класса следующим образом:

public abstract class EntityTypeConfiguration<TPrimaryKey, TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : Entity<TPrimaryKey>
{
    public void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.HasKey(e => e.Id);

        builder.Property(e => e.Id)
            .IsRequired()
            .HasMaxLength(13)
            .HasValueGenerator<PrimaryKeyValueGenerator>();

        // Apply the generic interface properties
        builder.ApplyInterfaceFields<TPrimaryKey, TEntity>();

        // Apply any additional configuration
        this.OnConfigure(builder);
    }

    protected abstract void OnConfigure(EntityTypeBuilder<TEntity> builder);
}

// In an extension class, I have
public static void ApplyInterfaceFields<TPrimaryKey, TEntity>(this EntityTypeBuilder<TEntity> builder) where TEntity : Entity<TPrimaryKey>
{
    // Check other implementations (removed for brevity)

    // IStatusField implementation
    if (HasFieldType<TEntity, IStatusField<>())
    {
        builder.Property("Status")
            .HasMaxLength(255)
            .HasConversion(new EnumToStringConverter<>());
    }

}

На момент проверки реализации IStatusField я ничего не знаю об указанном типе generi c. Я думаю, что это может быть большая проблема ...

Ответы [ 2 ]

0 голосов
/ 19 марта 2020

Вместо того, чтобы пытаться разобраться с разрешением аргументов типа generi c из ничего, вы можете рассмотреть возможность приближения к нему с противоположной стороны, получив список интерфейсов, реализованных TEntity, отфильтровав его для поиска IStatusField , Найдя поле, вы можете получить его аргументы типа 'generi c и передать их в EnumToStringConverter:

var statusField = typeof(TEntity)
    .GetInterfaces()
    .FirstOrDefault(x => x.Name.StartsWith("IStatusField"));

Заданное значение TEntity : IStatusField<ConsoleColor>:

statusField.GenericTypeArguments = [ typeof(System.Color) ]

Оттуда, хотя вы еще не закончили; вы все равно должны создать экземпляр универсального c типа EnumToStringConverter<System.Color>. Это довольно просто и изложено здесь .

Редактировать: я понял, что, поскольку вы будете вызывать конструктор, это не совсем 1016 * совсем то же самое. Вот как вы могли бы выполнить sh это:

var statusField = typeof(TEntity)
            .GetInterfaces()
            .FirstOrDefault(x => x.Name.StartsWith("IStatusField"));

        if (statusField != null)
        {

            var enumType = statusField.GenericTypeArguments[0]; // get the IStatusField<T> value

            // get the default constructor after supplying generic type arg
            var converterType = typeof(EnumToStringConverter<>)
                .MakeGenericType(enumType)
                .GetConstructors()[0];

            // invoke the constructor. Note the null (optional) param
            dynamic converter = converterType.Invoke(new Object[1]);

                builder.Property("Status")
                    .HasMaxLength(255)
                    .HasConversion(converter);
        }
0 голосов
/ 19 марта 2020

Хорошо, так что мне удалось решить проблему.

Нужно немного привести в порядок и немного проверить ошибки, но в самой грубой форме:

private static bool HasFieldType<TEntity>(Type interfaceType)
{
    var interfaces = typeof(TEntity).GetTypeInfo().ImplementedInterfaces;

    // Iterate through the interfaces
    foreach (var intf in interfaces)
    {
        // Compare interface names
        if (intf.FullName.Contains(interfaceType.FullName))
        {
            return intf.IsAssignableFrom(typeof(TEntity));
        }
    }

    return false;
}

Что теперь позволяет это работать:

// IStatusField implementation
if (HasFieldType<TEntity>(typeof(IStatusField<>)))
{
    builder.Property("Status")
        .HasMaxLength(255)
        .HasConversion<string>();
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...