Search code examples
c#entity-framework-coretype-conversionfunc

Can I convert a .ConvertToProvider from Func<object, object> to a more precise type?


My table T_SHIP has a column FULL_TANK, containing "Y" if the ship's fuel tank is full, "N" if it isn't, and Null if the ship doesn't have a tank, such as a sailing ship.

I define an entity property for that column as such:

p_etbTable.Property(t => t.HasFullTank).HasColumnName("FULL_TANK")
                                       .HasColumnType("varchar(1)")
                                       .HasConversion(ConversionsHelper.BooleanToY_N);

with BooleanToY_N a simple ValueConverter object:

public static ValueConverter<bool?, string> BooleanToY_N =
        new ValueConverter<bool?, string>(b => b.HasValue ? (b.Value ? "Y" : "N") : null,
                                          s => s == "Y");

In my code, I want to be able to access the values "Y" and "N" directly from BooleanToY_N, as an alternative to constants.

IConventionAnnotation z_annConverterHasFullTank = 
            ((IConventionAnnotatable)new MyDbContext().Model
                                        .GetEntityTypes()
                                        .FirstOrDefault(et => et.ClrType == typeof(TShip))
                                        .GetProperties()
                                        .First(pr => pr.Name == nameof(TShip.HasFullTank)))
                                        .GetAnnotation(nameof(EF_Storage.ValueConversion.ValueConverter));
EF_Storage.ValueConversion.ValueConverter<bool?, string> z_vcoConverterHasFullTank =
            (EF_Storage.ValueConversion.ValueConverter<bool?, string>)z_annConverterHasFullTank.Value;

Func <object, object> z_funConversionHasFullTank = z_vcoConverterHasFullTank.ConvertToProvider.;

and then use z_funConversionHasFullTank.Invoke() by passing it false or true to get "N" or "Y", respectively, then use .ToString() to convert it to a string.

This works quite well, but to simplify the use of z_funConversionHasFullTank, I'd like to define it as Func<bool?, string>.

When I do, the compiler informs me that it cannot convert Func<object, object> to Func<bool?, string>. Still, since there's a Func<bool?, string> in there I feel I should be able to get it out. Does anyone have an idea how that could be done?


Solution

  • Your expression is of the wrong type, must be:

    ValueConverter<bool?, string?>
    

    Because the string can also be null. Then it has a bug, it won't return null when the provider (string) is null but false instead:

    new ValueConverter<bool?, string?>(
        b => b.HasValue ? (b.Value ? "Y" : "N") : null, 
        s => s == null ? null : s == "Y"
    );
    

    (Or s => s == null ? null : (bool?)s == "Y" for earlier C# versions).

    Finally, you're right in that ConvertToProvider is a Func<object?, object?> and that you can't cast that to Func<bool?, string?>, but luckily there's also a ConvertToProviderExpression which is an Expression<Func<bool?, string?>>:

    Func<bool?, string?> toProvider = z_vcoConverterHasFullTank.
        ConvertToProviderExpression.Compile();