Анонимный тип в LINQ - PullRequest
       44

Анонимный тип в LINQ

2 голосов
/ 01 февраля 2011

Я пытаюсь получить анонимный объект из запроса:

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 элементов, я бы хотел, чтобы name имело значение NONE, если больше 1 элемента, тогда должно быть выдано исключение или что-то в этом роде.

Возможно ли вообще добиться чего-то подобного? Спасибо за помощь.

Ответы [ 3 ]

7 голосов
/ 01 февраля 2011

Вместо .ToList() используйте

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

Редактировать: Изменение ответа на основе комментария.

Также я бы рекомендовал не помещать такую ​​логику в Linq-to-SQL. Иногда это может привести к большой части крайне неоптимизированного кода SQL и, если вы не возражаете против некоторых проблем с производительностью, может привести к гораздо более медленному выполнению SQL.

2 голосов
/ 01 февраля 2011

Этого можно добиться, используя 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")
};
0 голосов
/ 08 октября 2018

Теоретически вы НЕ МОЖЕТЕ.

Если у вас нет типа для проверки свойств лямбды.

Хитрость заключается в том, чтобы преобразовать ваш анонимный объект в json и десериализовать его в известныйвведите, что вы должны иметь заранее.

Будьте осторожны при работе с ядром EF, потому что ваш запрос linq будет выполняться на стороне КЛИЕНТА !!.

Это означает, что все записи будут получены из вашего dbset иоценивать на клиенте.

НЕ использовать подобный код на EF dbset iquerables.

В любом случае, я скопирую код некоторых методов расширения, чтобы сделать это так.

Имея определенныйвведите как ...

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

Еще раз ... позвольте мне сказать, что если вы запрашиваете анонимный объект в памяти, все может быть в порядке, но будьте очень осторожны, если ваш iquerable происходит из ядра EF, так какоценка на стороне клиентанедопустимо.

Пожалуйста, включите предупреждение об исключении выброса, когда оценка кода на стороне клиента происходит в вашем базовом коде EF, который считается вашим DbContextOptionsBuilder, чтобы EF Core не выполнял код оценки на стороне клиента.Вы можете сделать это следующим образом.

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