Home > Net >  Is it possible to convert reference type to value type in Entity Framework?
Is it possible to convert reference type to value type in Entity Framework?

Time:10-08

I'd like to create wrapper class for int to use it in my entities (it would be used in many entities so I want to make it as easy to use as possible) and configure Entity Framework to treat it as an integer in SQL (to avoid creating new table only to store those values) e.g.:

public class SomeEntity
{
    public long Id { get; set; }

    public BoundedInteger SomeProperty { get; set; }
}

public class BoundedInteger
{
    public const int MAX_VALUE = 100;
    public const int MIN_VALUE = 0;

    private int _value;

    private int Value
    {
        get => _value;
        set
        {
            if (value < MIN_VALUE || value > MAX_VALUE)
            {
                throw new ArgumentOutOfRangeException(nameof(value));
            }
            _value = value;
        }
    }
}

SomeEntity table schema:

Id INTEGER;
SomeProperty INTEGER; // (Use BoundedInteger.Value as SomeProperty value in SQL)

CodePudding user response:

Yes, it is possible. What you need to do is a value converter.

As an example, I have this RowVersion struct that, if I remember correctly, I have only ever tested in SQL Server and there was one tiny thing to correct. But anyway, not important for this.

Basically, what I wanted was to encapsulate the various types of timestamps from the several DB engines into this single struct. You need to create a value converter for it. It is super simple.

First create the value converter. I do this in the DbContext and save it as a property to reuse in as many columns/properties as needed.

    public partial class DataContext : DbContext
    {
        #region Properties
        // I deleted some irrelevant stuff from here.

        /// <summary>
        /// Gets the byte array-to-RowVersion converter.
        /// </summary>
        public ValueConverter<RowVersion, byte[]> RowVersionConverter { get; }

        #endregion

        #region Constructors
        /// <summary>
        /// Creates a new instance of this class.
        /// </summary>
        /// <param name="options">Options containing database connection information.</param>
        public DataContext
        (
            IOptions<SqlServerOptions> options,
            IDbConnectionFactory connection
        )
        {
            Logger = Log.ForContext<DataContext>();
            Options = options.Value;
            DbConnectionFactory = connection;

            RowVersionConverter = new ValueConverter<RowVersion, byte[]>
            (
                v => v.Value.Reverse().ToArray(),
                v => new RowVersion(v)
            );
        }
        #endregion
    }

I create in the DbContext's constructor a simple value converter that is assisted by the presence of a RowVersion constructor that takes the raw data.

Now all you have to do is specify its usage.

        partial void ConfigureLocationModel(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Location>(b =>
            {
                // Other properties here.
                b.Property(m => m.RowVersion)
                    .HasColumnName("RowVersionCd")
                    .HasConversion(RowVersionConverter) // <---- Here
                    .IsRowVersion().IsConcurrencyToken();
            });
        }
  • Related