Type Converter / Generated Model Builder

Posts   
 
    
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 14-Aug-2019 20:33:40   

To me this seems like a bug with the expected generated output.

I built out a Type Converter for us since out of the box what we needed wasn't an option.

In the model builder, this was generated


config.Property(t => t.RowVersion).HasConversion(new assuresme.Core.TypeConverters.ByteToIntConverter).ValueGeneratedOnAddOrUpdate();

When this is what should be expected


config.Property(t => t.RowVersion).HasConversion<TypeConverters.ByteToIntConverter>().ValueGeneratedOnAddOrUpdate();

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 09:29:22   

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 12:03:35   

Otis wrote:

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

Sorry, yes it's EF Core 2.2 and the result is posted thanks.

If you pass in an instances then it would expect (new TypeConverter()) rather than (new TypeConverter) correct?

JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 12:05:41   

Otis wrote:

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

If you are expected a Value Converter class, why derive from TypeConverter?

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.builders.propertybuilder.hasconversion?view=efcore-2.1

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 12:10:40   

JSStudz wrote:

Otis wrote:

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

Sorry, yes it's EF Core 2.2 and the result is posted thanks.

If you pass in an instances then it would expect (new TypeConverter()) rather than (new TypeConverter) correct?

True, good catch. Weird that our buildtests don't catch that. Will look into that, as it should produce the () after it of course

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 12:11:09   

JSStudz wrote:

Otis wrote:

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

If you are expected a Value Converter class, why derive from TypeConverter?

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.builders.propertybuilder.hasconversion?view=efcore-2.1

I don't understand the question, sorry

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 12:12:16   

Otis wrote:

JSStudz wrote:

Otis wrote:

This is with EF Core 2.2 I presume? (it helps if you give a bit more info, e.g. what the result was wink )

We use this approach: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions#the-valueconverter-class

So we pass an instance of the converter to the HasConversion method. Did it fail in your situation?

If you are expected a Value Converter class, why derive from TypeConverter?

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.builders.propertybuilder.hasconversion?view=efcore-2.1

I don't understand the question, sorry

TypeConverter isn't supported in .HasConversion(), ValueConverter is.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 12:20:01   

Ah I think I see what's going on. We have default system type converters which are mapped to EF type converters, and by using the system type converter in the designer, you then get ,through the mapping. the real type converter emitted in the code.

A custom type converter has a problem here, indeed, as it needs to derive from TypeConverter to be useful in the designer, but doing so makes it useless at runtime in EF Core, as there it needs to derive from ValueConverter.

This is something we can't solve atm, but the lack of () is a bug we need to address first. When that's fixed (if we can reproduce it too), we can look at a workaround for this type converter. I'll get back to you.

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 12:21:13   

Otis wrote:

Ah I think I see what's going on. We have default system type converters which are mapped to EF type converters, and by using the system type converter in the designer, you then get ,through the mapping. the real type converter emitted in the code.

A custom type converter has a problem here, indeed, as it needs to derive from TypeConverter to be useful in the designer, but doing so makes it useless at runtime in EF Core, as there it needs to derive from ValueConverter.

This is something we can't solve atm, but the lack of () is a bug we need to address first. When that's fixed (if we can reproduce it too), we can look at a workaround for this type converter. I'll get back to you.

Thank you. Sort of a huge oversight for EF Core, I can make it work with extending the ModelBuilder.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 12:37:21   

The () missing is caused by the fact for EF Core we support out of the box only system type converters, which are then mapped on valueconverters during code generation (see: https://www.llblgen.com/Documentation/5.6/Entity%20Framework/VCore22/SupportedConstructs.htm#value-conversions-through-type-converters.) We added the () in the mapping as the name was there just for code generation. This way the () was always present but indeed using a custom type converter then doesn't work. We'll adjust the template.

So it was by design for now as we needed to deal with the difference in base type.

That said, we think there's a workaround. (which still requires the updated template btw): have a 'system typeconverter' for the type converter, which derives from TypeConverter and is simply used to make the designer work with it. The type name is the same as the real type converter. When code is generated it will emit the type converter in question into the code, but there you reference the assembly with the real type converter (which derives from valueconverter).

We'll test this with your conversion to see if we can achieve that.

A system type converter looks like this:


using System;
using System.ComponentModel;

namespace SD.LLBLGen.Pro.TypeConverters
{
    /// <summary>
    /// Definition of the Int16DecimalConverter. This converter uses 'Int16' as its core type and converts any decimal value to and from Int16
    /// </summary>
    /// <remarks>This class defines a System Type Converter, which means it is not able to convert any value, but defines which types can be
    /// converted to/from using this type converter. System type converters are used by the LLBLGen Pro designer to define type conversions
    /// and are mapped to real type converters / type conversion code by framework definition files for the target frameworks which support
    /// the type converters.
    /// <br/><br/>
    /// Accepted types are: Decimal</remarks>
    [Description("Converter with as core type System.Int16, for mapping a field with a .NET type System.Int16 onto a decimal database field")]
    public class Int16DecimalConverter : SystemTypeConverterBase
    {
        /// <summary>
        /// Checks the type if it's usable to convert to/from for this type converter.
        /// </summary>
        /// <param name="typeToCheck"></param>
        /// <returns>
        /// true if the type is usable for this type converter, false otherwise
        /// </returns>
        protected override bool CheckFromType(Type typeToCheck)
        {
            return typeToCheck == typeof(decimal);
        }


        /// <inheritdoc />
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(short);
        }


        /// <summary>
        /// Creates an instance of the Type that this <see cref="T:System.ComponentModel.TypeConverter"/> is associated with (short)
        /// </summary>
        /// <param name="context">ignored.</param>
        /// <param name="propertyValues">ignored.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> of type short. It always returns 0 for this converter.
        /// </returns>
        public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
        {
            return (short)0;
        }
    }
}

So it's a simple type which can return to the designer the right types/values for what it deals with. SystemTypeConverterBase is located in SD.LLBLGen.Pro.TypeConverters.dll, an assembly shipped with the designer in the folder <installation folder>\TypeConverters.

We'll get back to you regarding the updated template shortly.

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 13:04:56   

Thank you for your help, Otis.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 13:44:32   

Byte to Int is built-in, it turns out. We'll emit a CastingConverter:


config.Property(t => t.ReorderLevel).HasConversion(new Microsoft.EntityFrameworkCore.Storage.ValueConversion.CastingConverter<Nullable<System.Byte>, System.Int16>());

This means you don't need the converter if you want to convert from model type byte to db type int. I don't know if you need specifics in your converter, if not, that might be the route to take.

The system type converter route is sadly not going to work as our system has a filter which filters on system type converter mappings, and as a custom dll with a system type converter doesn't have that, it will ignore it. This is of course understandable, but in this case it's a bit of a problem.

This thus requires a normal TypeConverter class for the designer and a ValueConverter type at runtime. It's not a lot of work, but a bit more complexity than a system type converter. We'll get back to you.

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 13:52:05   

Otis wrote:

Byte to Int is built-in, it turns out. We'll emit a CastingConverter:


config.Property(t => t.ReorderLevel).HasConversion(new Microsoft.EntityFrameworkCore.Storage.ValueConversion.CastingConverter<Nullable<System.Byte>, System.Int16>());

This means you don't need the converter if you want to convert from model type byte to db type int. I don't know if you need specifics in your converter, if not, that might be the route to take.

The system type converter route is sadly not going to work as our system has a filter which filters on system type converter mappings, and as a custom dll with a system type converter doesn't have that, it will ignore it. This is of course understandable, but in this case it's a bit of a problem.

This thus requires a normal TypeConverter class for the designer and a ValueConverter type at runtime. It's not a lot of work, but a bit more complexity than a system type converter. We'll get back to you.

I need Int to Byte as well which isn't supported. We have a RowVersion of type sql timestamp that needs to be converted to int for digestion at the client.

TypeConverters are incompatible with EF Core 2.2.

From a ModelBuilder perspective, it's as simple as


var converter = new ValueConverter<long, byte[]>(
    v => BitConverter.GetBytes(v),
    v => BitConverter.ToInt64(v, 0)
);

config.property(p => p.RowVersion).HasConversion(converter);

However, the llblgen designer still requires a converter when I want to go from db type timestamp to int64(long). Probably should be noted that it's not supported currently within your designer.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 14:17:40   

Attached you'll find:

  • fixed template. Copy into: <installation folder>\Frameworks\Entity Framework\Templates\VCore\C#

  • Example typeconverter setup, where you have a typeconverter variant of your type converter you'll be using at runtime and the runtime ValueConverter. You copy the SystemTypeConverter.dll into the <installationfolder>\TypeConverters folder, or specify the folder it's in in the project settings -> Entity Model -> General -> Additional type converter folder

I know it's for this case easy in code, however to define conversions like this without compiling C# at the designer level isn't as simple, hence it works with precompiled assemblies. We could emit abstract methods to define the conversion as well, but that would give uncompilable code after code generation which is confusing for users (as they likely didn't read the documentation to the letter and thus have missed that they need to specify a conversion in code)

As said, the concept of value conversions/typeconverters is supported through predefined system conversions for the majority of cases and we went this route as we indeed have the base class problem. We documented this in the documentation that the list given was the list of converters supported out of the box. That doesn't mean it should be limited to those, but for now it is the case. Defining a class to make the designer happy however is a workaround for the cases where this is needed.

The problem with ValueConverter is that it pulls in 137 files from nuget for just the reference to the right dll to be able to use ValueConverter in code, so it's sadly not something we can add as 'use that too if TypeConverter isn't supported'. I wished MS would have picked a type that was already there, but alas...

Timestamp <-> int* isn't supported out of the box indeed, as it would require specific definitions what is considered 'valid' as byte array for the conversion.

Attachments
Filename File size Added on Approval
EFCore22Converters.zip 7,427 15-Aug-2019 14:17.46 Approved
modelBuilder.lpt 22,077 15-Aug-2019 14:17.57 Approved
Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 14:36:15   

Otis wrote:

Attached you'll find:

  • fixed template. Copy into: <installation folder>\Frameworks\Entity Framework\Templates\VCore\C#

  • Example typeconverter setup, where you have a typeconverter variant of your type converter you'll be using at runtime and the runtime ValueConverter. You copy the SystemTypeConverter.dll into the <installationfolder>\TypeConverters folder, or specify the folder it's in in the project settings -> Entity Model -> General -> Additional type converter folder

I know it's for this case easy in code, however to define conversions like this without compiling C# at the designer level isn't as simple, hence it works with precompiled assemblies. We could emit abstract methods to define the conversion as well, but that would give uncompilable code after code generation which is confusing for users (as they likely didn't read the documentation to the letter and thus have missed that they need to specify a conversion in code)

As said, the concept of value conversions/typeconverters is supported through predefined system conversions for the majority of cases and we went this route as we indeed have the base class problem. We documented this in the documentation that the list given was the list of converters supported out of the box. That doesn't mean it should be limited to those, but for now it is the case. Defining a class to make the designer happy however is a workaround for the cases where this is needed.

The problem with ValueConverter is that it pulls in 137 files from nuget for just the reference to the right dll to be able to use ValueConverter in code, so it's sadly not something we can add as 'use that too if TypeConverter isn't supported'. I wished MS would have picked a type that was already there, but alas...

Timestamp <-> int* isn't supported out of the box indeed, as it would require specific definitions what is considered 'valid' as byte array for the conversion.

Really appreciate the quick response on this and the help. I totally understand the short comings of Microsoft, one of the main reasons we are using your product to begin with.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2019 14:41:13   

No problem simple_smile Please let us know if this doesn't fix/solve your problem. We will work on make this easier in the future, e.g. with an interface which the designer understands.

Frans Bouma | Lead developer LLBLGen Pro
JSStudz
User
Posts: 42
Joined: 08-Jul-2019
# Posted on: 15-Aug-2019 16:10:21   

Otis wrote:

No problem simple_smile Please let us know if this doesn't fix/solve your problem. We will work on make this easier in the future, e.g. with an interface which the designer understands.

This will work in the meantime, thank you.