LINQ 中的匿名类型

问题描述 投票:0回答:3

我正在尝试从查询中获取匿名对象:

var myList = from td in MyObjectList
             select new
             {
                 a = td.a,
                 b = td.b,
                 c = td.c,
                 name = (from r in contex.NewList
                         where r.aa  == td.a && r.bb == td.b
                         select r.Name).ToList()
             };

我希望 name 具有 r.Name 值,因为我希望名称列表仅包含一个元素。如果它包含 0 个元素,我希望 name 的值为 NONE,如果超过 1 个元素,则应抛出异常或其他内容。

有可能实现这样的目标吗?感谢您的帮助。

c# linq linq-to-sql .net-4.0 anonymous-types
3个回答
7
投票

代替

.ToList()
使用

.SingleOrDefault() ?? (td.a == 0 ? "XNone" : "None")

编辑:根据评论更改了答案。

我还建议不要将此类逻辑放入 Linq-to-SQL 中。有时,这可能会导致大量高度未优化的 SQL 代码,并且除非您不介意某些性能问题,否则可能会导致 SQL 执行速度慢得多。


2
投票

您可以使用

SingleOrDefault
和表达式中的临时变量来实现这一点。像这样的东西:

var myList =     
from td in MyObjectList
let nameValue = contex.NewList
                    .Where(r => r.aa== td.a && r.bb == td.b)
                    .Select(r => r.Name)
                    .SingleOrDefault()
select new
{
    a = td.a,
    b = td.b,
    c = td.c,
    name = nameValue ?? "NONE"
};

更新:而不是提供与@Euphorics答案几乎相同的解决方案,我对代码进行了一些重组。我经常发现嵌套的 LINQ 表达式使内容的可读性降低。将理解语法转换为调用链可以改善这一点。

更新 2: 添加了一些要求,以下

select
应该可以解决问题:

select new
{
    a = td.a,
    b = td.b,
    c = td.c,
    name = nameValue ?? (td.a == 0 ? "XNone" : "None")
};

0
投票

理论上你不能。

除非你有一个可以检查 lambda 属性的类型。

技巧是将匿名对象转换为 json 并将其反序列化为您必须提前拥有的已知类型。

使用 EF core 时请小心,因为您的 linq 查询将在客户端执行!!.

这意味着将从您的数据库集中检索所有记录并在客户端上进行评估。

不要在 EF dbset 上使用这样的代码

IQueryable
s。

无论如何,我都会复制一些扩展方法代码来做到这一点。

有一个定义的类型,例如...

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
}

扩展将任何对象转换为 json 字符串...

public static class ObjectExtensions
{
    public static string ToJson(this object source)
    {
        return JsonConvert.SerializeObject(source, Formatting.None);
    }
}

扩展将任何 json 字符串转换为类型化对象...

public static class StringExtensions
{
    public static T FromJson<T>(this string source) where T : class
    {
        return JsonConvert.DeserializeObject<T>(source);
    }
}

一些 xUnit 测试

[Fact]
public void AddSelectTest()
{
    var data = new[]
    {
        new {Id = 01, Name = "Engineering", GroupName = "Engineering and Development"},
        new {Id = 02, Name = "Tool Design", GroupName = "Tool Design and Research"},
        new {Id = 03, Name = "Sales", GroupName = "Sales and Marketing"},
        new {Id = 04, Name = "Marketing", GroupName = "Marketing and Sales"},
        new {Id = 05, Name = "Purchasing", GroupName = "Inventory Management"},
        new {Id = 06, Name = "Research and Development", GroupName = "Development and Research"},
        new {Id = 07, Name = "Production", GroupName = "Manufacturing and Production"},
        new {Id = 08, Name = "Production Control", GroupName = "Control and Production"},
        new {Id = 09, Name = "Human Resources", GroupName = "Human Resources and Administration"},
        new {Id = 10, Name = "Finance", GroupName = "Finance and Executive General"},
        new {Id = 11, Name = "Information Services", GroupName = "Information Services and Administration"},
        new {Id = 12, Name = "Document Control", GroupName = "Document Control and Quality Assurance"},
        new {Id = 13, Name = "Quality Assurance", GroupName = "Administration and Quality Assurance"},
        new {Id = 14, Name = "Facilities and Maintenance", GroupName = "Maintenance and Facilities"},
        new {Id = 15, Name = "Shipping and Receiving", GroupName = "Receiving and Shipping"},
        new {Id = 16, Name = "Executive", GroupName = "Executive General and Administration"}
    };

    var queryable = data.AsQueryable();

    var first = queryable.Select(d => new { Id = d.Id, Name = d.Name }).FirstOrDefault(d => d.ToJson().FromJson<Department>().Id == 1);

    Assert.True(first != null, "Expected a department value but 'null' was found.");
}

再次...让我说,如果您在内存对象中查询匿名,那可能没问题,但如果您的

IQueryable
来自 EF Core,则要非常小心,因为将会发生客户端评估。

当您的 EF Core 代码发生客户端评估时,请启用抛出异常警告(认为您的

DbContextOptionsBuilder
),以防止 EF Core 执行客户端评估代码。你可以这样做..

builder.UseSqlServer(connection, sql =>
                {
                    sql.EnableRetryOnFailure();
                    sql.MigrationsAssembly(assembly);
                })
       .UseQueryTrackingBehavior(track ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking)
                .ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));
© www.soinside.com 2019 - 2024. All rights reserved.