LINQ中的匿名类型

时间:2011-02-01 09:27:34

标签: c# linq linq-to-sql .net-4.0 anonymous-types

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

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()
             };

我想将名称设为r.Name值因为我希望名称列表只包含一个元素。如果它包含0个元素,我想命名为NONE,如果超过1个元素则应该抛出异常或其他东西。

甚至有可能实现这样的目标吗?谢谢你的帮助。

3 个答案:

答案 0 :(得分:7)

而不是.ToList()使用

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

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

另外,我建议不要将这样的逻辑放入Linq-to-SQL中。有时这会导致大量高度未经优化的SQL代码,除非你不介意一些性能问题,否则会导致SQL执行速度慢得多。

答案 1 :(得分: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 answer几乎相同的解决方案,我重新构建了一些代码。我经常发现嵌套的LINQ表达式使得可读性降低。将理解语法转换为调用链可以改善这一点。

更新2:并增加了一些要求,以下select应该可以解决问题:

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

答案 2 :(得分:0)

理论上你不能。

除非您有可以从其中检查lambda属性的类型。

诀窍是将匿名对象转换为json并将其反序列化为事先必须具有的已知类型。

使用EF内核时要小心,因为您的linq查询将在客户端执行!!

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

请勿在EF dbset可查询对象上使用此类代码。

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

具有定义的类型,例如...

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.");
}

再一次...让我说一次,如果您要查询内存对象中的匿名对象就可以了,但是如果您的可查询性来自EF核心,则要非常小心,因为将会进行客户端评估。

请以DbContextOptionsBuilder为基础,在EF核心代码上进行客户端评估时启用抛出异常警告,以防止EF Core执行客户端评估代码。您可以按照以下步骤进行。

            builder.UseSqlServer(connection, sql =>
                {
                    sql.EnableRetryOnFailure();
                    sql.MigrationsAssembly(assembly);
                })
                .UseQueryTrackingBehavior(track ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking)
                .ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));