【问题标题】:Project a Query onto an anonymous Dictionary<string,int>将查询投影到匿名 Dictionary<string,int>
【发布时间】:2016-08-24 02:03:19
【问题描述】:

我正在尝试检查数据库中的实体是否有任何外键关系,以便我可以通知用户该实体可以或不能删除。

我知道这可以在回滚事务中完成,但是我想通知用户有多少引用以及他们在哪里帮助他们决定删除实体。

我试图避免将整个导航集合加载到内存中以获取此数据,因为它可能很大。所以,鉴于此,我可以制定这个简单的查询来首先确定是否有任何引用:

private bool CanDeleteComponent(int compId)
{
    var query = _Context.Components.Where(c => c.ComponentID == compId)
        .Select(comp => new
        {
            References = comp.Incidents.Any() &&
            comp.Drawings.Any() &&
            comp.Documents.Any() &&
            comp.Tasks.Any() &&
            comp.Images.Any() &&
            comp.Instructions.Any()
        });
    var result = query.FirstOrDefault();
    if (result != null)
    {
        return !result.References;
    }
    return true;
}

这会执行一系列SELECT COUNT(*) FROM &lt;TABLE&gt; WHERE... 查询。

现在,我想提供一些有关参考数量的更多信息。理想情况下,我想返回一个带有引用数据名称和相关计数的字典。这样我就可以遍历结果,而不是访问匿名类型的单个属性。但是,我尝试过的结果是异常:

var query = _Context.Components.Where(c => c.ComponentID == compId)
        .Select(comp => new Dictionary<string, int>
        {
            {"Events", comp.Incidents.Count()},
            {"Drawings", comp.Drawings.Count()},
            {"Documents", comp.Documents.Count()},
            {"Tasks", comp.Tasks.Count()},
            {"Images", comp.Images.Count()},
            {"Instructions", comp.Instructions.Count()},
        });
    var result = query.FirstOrDefault();
    return query.Any(fk => fk.Value > 0);

引发的异常是:

A first chance exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll
Additional information: Only list initializer items with a single element are supported in LINQ to Entities.

有没有什么办法可以让我返回某种 IEnumerable 而不是匿名类型?

谢谢

编辑 我目前在我的上下文中禁用了延迟加载。如果有没有打开延迟加载的解决方案,将不胜感激。

【问题讨论】:

    标签: c# entity-framework dictionary ienumerable


    【解决方案1】:

    您不能在SELECT 语句中构建Dictionary&lt;K,V&gt;,这就是您得到System.NotSupportedException 的原因。可以先通过查询得到单个Component,然后在内存中构建字典。

    var comp = _Context.Components.SingleOrDefault(c => c.ComponentID == compId);
    var dict = new Dictionary<string, int>()
    {
        { "Events", comp.Incidents.Count()},
        { "Drawings", comp.Drawings.Count()},
        { "Documents", comp.Documents.Count()},
        { "Tasks", comp.Tasks.Count()},
        { "Images", comp.Images.Count()},
        { "Instructions", comp.Instructions.Count()}
    };
    

    编辑如果你没有使用延迟加载,你可以在查询中显式.Include属性:

    var comp = _Context.Components
        .Include(c => c.Incidents)
        ...
        .SingleOrDefault(c => c.ComponentID == compId);
    

    【讨论】:

    • 谢谢,如果我有 LazyLoadingEnabled = true,我可以看到这将起作用。有什么办法可以关掉吗?
    • 谢谢,但现在这会加载所有导航数据,这可能会非常大并且内存密集,这是我试图避免的。最终目标是只对每个 FK 表执行 SELECT COUNT() 查询
    • @Simon Try .Where(xxx).Select(comp =&gt; new { Events = comp.Incidents.Count, Drawings = comp.Drawings.Count, Document = comp.Documents.Count, Tasks = comp.Tasks.Count, Images = comp.Images.Count, Instructions = comp.Instructions.Count }).SingleOrDefault();
    • 谢谢 Danny,似乎没有办法在查询中完成所有操作,所以这是下一个最好的方法。
    【解决方案2】:

    有没有什么办法可以让我返回某种 IEnumerable 而不是匿名类型?

    实际上有,虽然我不确定你是否会喜欢生成的 SQL(与使用匿名类型的 SQL 相比)。

    var query = _Context.Components.Where(c => c.ComponentID == compId)
        .SelectMany(comp => new []
        {
            new { Key = "Events", Value = comp.Incidents.Count() },
            new { Key = "Drawings", Value = comp.Drawings.Count() },
            new { Key = "Documents", Value = comp.Documents.Count() },
            new { Key = "Tasks", Value = comp.Tasks.Count() },
            new { Key = "Images", Value = comp.Images.Count() },
            new { Key = "Instructions", Value = comp.Instructions.Count() },
         }.ToList());
    
    var result = query.ToDictionary(e => e.Key, e => e.Value);
    
    return query.Any(fk => fk.Value > 0);
    

    【讨论】:

    • 我无法让它工作。对于初学者,在我将 new 放在每个匿名对象之前,该示例不会为我编译。其次,我收到错误The array type '&lt;&gt;f__AnonymousType1'2[System.String,System.Int32][]' cannot be initialized in a query result. Consider using 'System.Collections.Generic.List'1[&lt;&gt;f__AnonymousType1'2[System.String,System.Int32]]' instead.
    • @erdomke 您在每个数组元素之前的new 是正确的。如果您使用Select 而不是SelectMany,那么您会遇到什么异常。在末尾添加 ToList() 使其在 SelectSelectMany 中都可以使用 - 请参阅更新。 EF是个奇怪的怪物:)
    • 你说得对,我设法掩盖了你对 SelectMany 的使用。该示例现在适用于我,但前提是我省略了您新添加的 ToList()。使用 ToList,我得到 LINQ to Entities does not recognize the method 'System.Collections.Generic.List'1[&lt;&gt;f__AnonymousType1'2[System.String,System.Int32]] ToList[&lt;&gt;f__AnonymousType1'2](System.Collections.Generic.IEnumerable'1[&lt;&gt;f__AnonymousType1'2[System.String,System.Int32]])' method, and this method cannot be translated into a store expression. 感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 2011-07-27
    • 1970-01-01
    • 2011-11-27
    • 1970-01-01
    • 2013-07-01
    • 2011-03-05
    相关资源
    最近更新 更多