简体   繁体   中英

Anonymous Type in LINQ

I'm trying to get anonymous object from query:

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

I would like name to have r.Name value cause I expect that name list contains only one element. If it contains 0 elements I would like name to have value NONE if more then 1 element then exception should be thrown or something.

Is it even possible to achieve something like that? Thanks for help.

Instead of .ToList() use

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

Edit: Changed anwer based on comment.

Also I would recomend not to put such logic into Linq-to-SQL. Sometimes this can result in big chunk of highly-unoptimized SQL code and, unless you dont mind some performance isues, can result in much slower SQL execution.

You can achieve that using SingleOrDefault and a temporary variable within the expression. Something like this:

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

Update: instead of presenting almost the same solution as @Euphorics answer , I've restructured the code a bit. I often find nested LINQ expressions making things less readable. Converting comprehension syntax into call chains could improve that.

Update 2: with some added requirements, the following select should do the trick:

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

Theoretically you CAN'T.

Unless you have a type from where you could inspect lambda properties.

The trick is to convert your anonymous object to json and deserialize it into a known type that you must have in advance.

Caution working with EF core because your linq query will be executed CLIENT side!!.

That means that all the records will be retrieved from your dbset and evaluate on client.

Do NOT use code like this on EF dbset iquerables.

Any away I will copy some extensions methods code to do it so.

Having a defined type like...

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

Extension convert any object to json string...

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

Extension convert any json string to typed object...

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

Some xUnit Test

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

Once again... let me say that if you are querying anonymous in memory object it could be ok, but be very cautious if your iquerable cames from EF core, since client side evaluation will happen.

Please enable throw exception warning when client side evaluation happens on your EF core code thought your DbContextOptionsBuilder, to prevent EF Core of executing client side evaluation code. You could do it follows..

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM