Я работаю с 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));