2

I am trying to convert a field on way in from the database, and also on its way out to the database. The tricky part is, I would like to use data from one column to affect data in a different column.

This has worked for me successfully when I hard-code some of the information needed, but I would like to use information from another column as part of the conversion process. Is that possible? I thought of using a dataservice inside the conversion, on the same context - but it would need the rows primary key, and might make it do an extra query for every record the result -- plus I couldn't even get that to work. Any ideas would be great, thanks for your time!

// This works:
var converter1 = new ValueConverter<DateTime, DateTime>(
            from => MyClass.MyFromMethod(from, "my static text"),
            to => MyClass.MyToMethod(to, "my other static text")
        );

// This does NOT work:  (I want to replace the static text above with somehow getting data from a different column in same row here)
var converter2 = new ValueConverter<DateTime, DateTime>(
            from => MyClass.MyFromMethod(from, entity.Property(e => e.OtherColumn).SomehowGetValue()),
            to => MyClass.MyToMethod(to, entity.Property(e => e.OtherColumn).SomehowGetValue())
        );

// called like this:
modelBuilder.Entity<MyTable>(entity =>
        {
            entity.ToTable("MyTable");
            entity.HasIndex(e => e.MyIdColumn, "MyIndex");
            //generated code here..
            entity.Property(e => e.MyDateColumn).HasColumnType("datetime")
                .HasConversion(converter1); //switching to converter2 ideally!
            //more generated code here..
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

1 Answers1

1

From the limitations section of Value Conversions:

There is currently no way to spread a conversion of one property to multiple columns or vice-versa

So what you are trying to do is not supported at the moment.

What I did for a low-impact scenario or one-off utilities in the past is to have an unmapped property in my model that does the conversion.

public class Foo
{
    public int FooId { get; set; }
    public int NeedsConversion { get; set; }
    public int ConvertInfo { get; set; }

    [NotMapped]
    public int DependsOnConversion
    {
        get
        {
            return DoConvert(this.NeedsConversion, this.ConvertInfo);
        }
        set
        {
            this.NeedsConversion = DoConvertBack(value, this.ConvertInfo);
        }
    }
}

The next part is just my personal opinion and there will people who disagree...

When I design databases I tend to think of then as a rather stupid storage layer whose sole purpose is to store data in a save and persistent way. That raw data is processed by what I call business logic layer.

Oftentimes conversions depend on some kind of business rule (e.g: If this value equals Foo we have to factor in Bar to the final value). I do not want my storage layer to have that kind of knowledge (that type of stuff goes in the business layer). I personally would only allow small, atomic conversions (The build-in converters are examples of what I mean by small and atomic) in the storage layer. Everything more complex than that goes inside the business layer (or an intermediate that translates the business layer into storage layer).

My approach for that looks like this:

/// The model class that is mapped by EF core
public class FooStorage
{
    public int FooId { get; set; }

    public int Bar { get; set; }

    public int Baz { get; set; }
}

public class StorageToBusiness
{
    public FooBusiness Convert(FooStorage storage)
    {
        return new FooBusiness
        {
            //Management tole me I have to multiply by this magic 42
            CalculatedFooBar = storage.Bar + storage.Baz * 42
        };
    }
}

/// The business object 
public class FooBusiness
{
    public int CalculatedFooBar { get; set; }
}

Thay way we have separated storage logic from business logic

MikeVe
  • 1,062
  • 8
  • 13