Checking Whether Class Implements Generic Interface

c# entity-framework-core generics interface

Question

I'm using the following generic function to determine whether a class implements a specified interface:

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

This works fine for the majority of the time.

However, I now have an interface which has a generic parameter:

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

And this causes the HasFieldType function to break with an unexpected use of unbound generic name error.

Ideally, I want to call the function like:

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

But this won't work as I'm not specifying the generic type for both the IStatusField<> or the EnumToStringConverter<>.

Is there any way around this?

UPDATE

This code forms part of a generic base IEntityTypeConfiguration class as follows:

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<>());
    }

}

At the point of checking for IStatusField implementation, I know nothing about the generic type specified. I think this may be the bigger problem...

1
2
3/19/2020 4:17:50 PM

Popular Answer

Instead of trying to wrestle with resolving generic type arguments from nothing, you might consider approaching it from the opposite direction, by getting a list of interfaces implemented by TEntity, filtering it to search for an IStatusField. Once you've located the field, you can get its' generic type arguments and pass those to your EnumToStringConverter:

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

Value given TEntity : IStatusField<ConsoleColor>:

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

From there though you're not done; you must still construct an instance of the generic type EnumToStringConverter<System.Color>. This is rather simple and outlined here.

Edit: I realized that because you'd be invoking a constructor, it's not quite the same. Here's how you'd accomplish this:

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
3/19/2020 6:40:32 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow