c# - 实体框架 Linq 查询在使用带有 ValueConverter 的自定义类型时由客户端评估
问题描述
我正在使用 Microsoft 的Entity Framework Core并尝试利用ValueConverters来允许我的数据库模型实体中的自定义类型。关键是要有我自己的类型,我可以自定义它并将其余代码与数据库中实际使用的类型隔离开来。
(遗憾的是,遗留代码直接访问模型实体而没有接口,所以这是我剩下的,除非我进行重大检修。)
它主要工作,但我的问题是实体框架无法将我的类型转换为数据库类型的where 子句 (可能是其他,但这是我遇到的),而是进行客户端评估,这显然是性能问题,因为所有候选人都被查询。
所以,我想知道是否有人遇到过这种情况,是否有解决方案,或者我是否必须尝试不同的方法。
如果你想要一些代码,它就在那里。我试图把它修整,所以实现有点奇怪,但它仍然以同样的方式失败。
让我们调用我的自定义结构类型ItemId
,使其包含一个字符串并允许从 long 或 string 创建它:
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 位数字 ID 的数据库的转换器。我强烈怀疑手写表达式是不必要的,因为内置转换器通常不使用它们并且它们似乎工作正常,但我添加了它们以试图解决我的问题:
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));
解决方案
推荐阅读
- automation - EarlGrey 可以用作只有 ipa 文件的测试工具吗?
- reactjs - Reactjs:无法使用“npm start”启动开发服务器
- r - 使用 Quanteda 清理语料库
- electron - 我的 spectron app.client 不包含所有方法
- javascript - 尝试运行 Node.js 应用程序时如何修复“语法错误:无效或意外令牌”
- r - 将 case_when 和 between 与对应阈值表一起使用
- html - 弹性项目中的锚标记尊重所有填充,没有 display:inline-block 属性的边距。这是为什么?
- python - 将结果从并行计算移动到 Python 中的最终数组
- wordpress - pubDate 未与 Wordpress 网站的时区同步
- angular - TinyMCE 角度代码和链接覆盖在角度反应形式中不起作用