Entity Framework Linq-запрос оценивается клиентом при использовании пользовательского типа с ValueConverter - PullRequest
0 голосов
/ 26 ноября 2018

Я работаю с Microsoft Entity Framework Core и пытался использовать ValueConverters , чтобы разрешить создание пользовательских типов в объектах модели моей базы данных.Суть в том, чтобы иметь свой собственный тип, который я могу настроить и который защищает остальную часть кода от типа, фактически используемого в базе данных.

(К сожалению, унаследованный код обращается к объектам модели напрямую безинтерфейс, так что это то, что у меня осталось, если я не сделаю значительный пересмотр.)

В основном это работает, но моя проблема в том, что Entity Framework не может конвертировать моиtype to database type для , где условие (возможно, другие, но это то, с чем я столкнулся) и вместо этого оценка на стороне клиента , которая, очевидно,проблема производительности, так как все кандидаты опрошены.

Итак, мне интересно, сталкивался ли кто-нибудь с этим и есть ли решение, или мне нужно попробовать что-то другое.


Если вам нужен какой-то код, вот он.Я попытался урезать его, поэтому реализация немного странная, но он все равно не работает таким же образом.

Давайте назовем мой пользовательский тип структуры ItemId, сделаем так, чтобы он содержал строку и позволял ей бытьсоздан из длинной или строки:

public struct ItemId
{
    public string Data;

    public ItemId(long data)
    {
        Data = data.ToString();
    }

    public ItemId(string data)
    {
        Data = data;
    }

    public override bool Equals(object obj)
    {
        return obj is ItemId itemId && Data == itemId.Data;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Data);
    }

    public static bool operator ==(ItemId id1, ItemId id2)
    {
        return id1.Data == id1.Data;
    }

    public static bool operator !=(ItemId id1, ItemId id2)
    {
        return !(id1== id1);
    }
}

Затем, есть мой конвертер для базы данных, которая хранит 64-битные числовые идентификаторы.Я сильно подозреваю, что рукописные выражения не нужны, поскольку встроенные преобразователи часто не используют их, и они, кажется, работают нормально, но я добавил их в попытке решить мою проблему:

public class ItemIdToLongConverter : ValueConverter<ItemId, long>
{
    public ItemIdToLongConverter(ConverterMappingHints mappingHints = null)
        : base(ToLong(), ToItemId(), mappingHints)
    { }

   protected static Expression<Func<ItemId, long>> ToLong()
    {
        var data = typeof(ItemId).GetField(nameof(ItemId.StringData));
        var tryParseMethod = typeof(long).GetMethod(
            nameof(long.TryParse),
            new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(long).MakeByRefType() });

        var param = Expression.Parameter(typeof(ItemId));
        var parsedVariable = Expression.Variable(typeof(long));

        return Expression.Lambda<Func<ItemId, long>>(
            Expression.Block(
                typeof(long),
                new[] { parsedVariable },
                Expression.Condition(
                    Expression.Call(
                        tryParseMethod,
                        Expression.Field(param, data),
                        Expression.Constant(NumberStyles.Any),
                        Expression.Constant(CultureInfo.InvariantCulture, typeof(IFormatProvider)),
                        parsedVariable),
                    parsedVariable,
                    Expression.Constant(default(long), typeof(long)))),
            param);
    }

    protected static Expression<Func<long, ItemId>> ToItemId()
    {
        var ctor = typeof(ItemId).GetConstructor(new[] { typeof(long) });

        var param = Expression.Parameter(typeof(long));

        return Expression.Lambda<Func<long, ItemId>>(
            Expression.Block(
                typeof(ItemId),
                Expression.New(ctor, param)
            ),
            param);
    }
}

Я регистрирую свой конвертер в модели следующим образом:

modelBuilder.Entity<MyTable>(entity =>
{
    ...
    entity.Property(e => e.ItemId).HasConversion(new ItemIdToLongConverter()).ValueGeneratedNever();
    ...
});

И вот запрос, который оценивается клиентом, потому что он не может преобразовать id в тип базы данных:

var id = new ItemId(100);
dbContext.MyTable.FirstOrDefault(x => x.ItemId == id);

Любопытно, что этот странно структурированный переводится хорошо:

var ids = Enumerable.Repeat(new ItemId(100), 1);
dbContext.MyTable.FirstOrDefault(x => ids.Contains(x.ItemId));
...