- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在尝试使用 EF Core 组织一些数据访问代码时,我注意到生成的查询比以前更糟糕,它们现在查询了不需要的列。基本查询只是从一个表中选择并将列的子集映射到 DTO。但是在重写它之后,现在所有的列都被提取了,而不仅仅是 DTO 中的那些。
我创建了一个最小示例,其中包含一些显示问题的查询:
ctx.Items.ToList();
// SELECT i."Id", i."Property1", i."Property2", i."Property3" FROM "Items" AS i
ctx.Items.Select(x => new
{
Id = x.Id,
Property1 = x.Property1
}
).ToList();
// SELECT i."Id", i."Property1" FROM "Items" AS i
ctx.Items.Select(x => new MinimalItem
{
Id = x.Id,
Property1 = x.Property1
}
).ToList();
// SELECT i."Id", i."Property1" FROM "Items" AS i
ctx.Items.Select(
x => x.MapToMinimalItem()
).ToList();
// SELECT i."Id", i."Property1", i."Property2", i."Property3" FROM "Items" AS i
ctx.Items.Select(
x => new MinimalItem(x)
).ToList();
// SELECT i."Id", i."Property1", i."Property2", i."Property3" FROM "Items" AS i
public class Item
{
public int Id { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public class MinimalItem
{
public MinimalItem() { }
public MinimalItem(Item source)
{
Id = source.Id;
Property1 = source.Property1;
}
public int Id { get; set; }
public string Property1 { get; set; }
}
public static class ItemExtensionMethods
{
public static MinimalItem MapToMinimalItem(this Item source)
{
return new MinimalItem
{
Id = source.Id,
Property1 = source.Property1
};
}
}
MinimalItem
只要直接在 Select 方法中创建 DTO,它也可以工作。但是最后两个查询获取所有列,即使它们与第三个查询完全相同,只是分别移动到构造函数或扩展方法。
最佳答案
这是 IQueryable
的基本问题。从一开始,经过这么多年没有开箱即用的解决方案。
问题是 IQueryable
翻译和代码封装/可重用性是相互排斥的。 IQueryable
翻译基于预先的知识,这意味着查询处理器必须能够“看到”实际代码,然后翻译“已知”的方法/属性。但是自定义方法/可计算属性的内容在运行时不可见,因此查询处理器通常会失败,或者在它们支持“客户端评估”的有限情况下(EF Core 仅针对最终预测)它们会生成低效的翻译,从而检索到很多比您的示例中需要的更多数据。
回顾一下,C# 编译器和 BCL 都不能帮助解决这个“核心问题”。一些第 3 方图书馆正试图以不同程度解决它 - LinqKit , NeinLinq和类似的。它们的问题在于,它们需要重构现有代码,才能调用特殊方法,如 AsExpandable()
。 , ToInjectable()
等等
最近我发现了一个小 gem ,叫做 DelegateDecompiler ,它使用另一个名为 Mono.Reflection.Core 的包将方法体反编译为其 lambda 表示。
使用它非常容易。安装后您需要做的就是用自定义提供的[Computed]
标记您的自定义方法/计算属性。或 [Decompile]
属性(只要确保你使用表达式样式实现而不是代码块),然后调用 Decompile()
或 DecompileAsync()
IQueryable
中某处的自定义扩展方法链。它不适用于构造函数,但支持所有其他构造。
例如,以您的扩展方法为例:
public static class ItemExtensionMethods
{
[Decompile] // <--
public static MinimalItem MapToMinimalItem(this Item source)
{
return new MinimalItem
{
Id = source.Id,
Property1 = source.Property1
};
}
}
ctx.Items.Decompile()
.Select(x => x.MapToMinimalItem())
.ToList();
// SELECT i."Id", i."Property1" FROM "Items" AS i
Decompile
,以便用自定义提供程序包装可查询的内容,以便能够预处理最终的查询表达式。
AsTracking
,
AsNoTracking
,
Include
/
ThenInclude
,所以它真的应该在他们之后调用等等。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.EntityFrameworkCore
{
public static partial class CustomDbContextOptionsExtensions
{
public static DbContextOptionsBuilder AddQueryPreprocessor(this DbContextOptionsBuilder optionsBuilder, IQueryPreprocessor processor)
{
var option = optionsBuilder.Options.FindExtension<CustomOptionsExtension>()?.Clone() ?? new CustomOptionsExtension();
if (option.Processors.Count == 0)
optionsBuilder.ReplaceService<IQueryTranslationPreprocessorFactory, CustomQueryTranslationPreprocessorFactory>();
else
option.Processors.Remove(processor);
option.Processors.Add(processor);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(option);
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.Infrastructure
{
public class CustomOptionsExtension : IDbContextOptionsExtension
{
public CustomOptionsExtension() { }
private CustomOptionsExtension(CustomOptionsExtension copyFrom) => Processors = copyFrom.Processors.ToList();
public CustomOptionsExtension Clone() => new CustomOptionsExtension(this);
public List<IQueryPreprocessor> Processors { get; } = new List<IQueryPreprocessor>();
ExtensionInfo info;
public DbContextOptionsExtensionInfo Info => info ?? (info = new ExtensionInfo(this));
public void Validate(IDbContextOptions options) { }
public void ApplyServices(IServiceCollection services)
=> services.AddSingleton<IEnumerable<IQueryPreprocessor>>(Processors);
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(CustomOptionsExtension extension) : base(extension) { }
new private CustomOptionsExtension Extension => (CustomOptionsExtension)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment => string.Empty;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo) { }
public override long GetServiceProviderHashCode() => Extension.Processors.Count;
}
}
}
namespace Microsoft.EntityFrameworkCore.Query
{
public interface IQueryPreprocessor
{
Expression Process(Expression query);
}
public class CustomQueryTranslationPreprocessor : RelationalQueryTranslationPreprocessor
{
public CustomQueryTranslationPreprocessor(QueryTranslationPreprocessorDependencies dependencies, RelationalQueryTranslationPreprocessorDependencies relationalDependencies, IEnumerable<IQueryPreprocessor> processors, QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext) => Processors = processors;
protected IEnumerable<IQueryPreprocessor> Processors { get; }
public override Expression Process(Expression query)
{
foreach (var processor in Processors)
query = processor.Process(query);
return base.Process(query);
}
}
public class CustomQueryTranslationPreprocessorFactory : IQueryTranslationPreprocessorFactory
{
public CustomQueryTranslationPreprocessorFactory(QueryTranslationPreprocessorDependencies dependencies, RelationalQueryTranslationPreprocessorDependencies relationalDependencies, IEnumerable<IQueryPreprocessor> processors)
{
Dependencies = dependencies;
RelationalDependencies = relationalDependencies;
Processors = processors;
}
protected QueryTranslationPreprocessorDependencies Dependencies { get; }
protected RelationalQueryTranslationPreprocessorDependencies RelationalDependencies { get; }
protected IEnumerable<IQueryPreprocessor> Processors { get; }
public QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext)
=> new CustomQueryTranslationPreprocessor(Dependencies, RelationalDependencies, Processors, queryCompilationContext);
}
}
IQueryPreprocessor
和
AddQueryPreprocesor
(类似于最近添加的拦截器)。如果 EF Core 将来添加该功能,我将对其进行更新。
DelegateDecompiler
进入 EF 核心:
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using DelegateDecompiler;
namespace Microsoft.EntityFrameworkCore
{
public static class DelegateDecompilerDbContextOptionsExtensions
{
public static DbContextOptionsBuilder AddDelegateDecompiler(this DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.AddQueryPreprocessor(new DelegateDecompilerQueryPreprocessor());
}
}
namespace Microsoft.EntityFrameworkCore.Query
{
public class DelegateDecompilerQueryPreprocessor : IQueryPreprocessor
{
public Expression Process(Expression query) => DecompileExpressionVisitor.Decompile(query);
}
}
DecompileExpressionVisitor.Decompile(query)
optionsBuilder.AddDelegateDecompiler();
OnConfiguring
覆盖,并且您的所有 EF Core LINQ 查询都将被预处理并注入(inject)反编译的主体。
ctx.Items.Select(x => x.MapToMinimalItem())
ctx.Items.Select(x => new
{
Id = x.Id,
Property1 = x.Property1
}
// SELECT i."Id", i."Property1" FROM "Items" AS I
ctx.Items
.Select(x => x.MapToMinimalItem())
.Where(x => x.Property1 == "abc")
.ToList();
关于c# - EF Core 在映射到 Select 中的对象时查询 SQL 中的所有列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62115690/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!