Передайте выражение для выбора linq - PullRequest
14 голосов
/ 17 марта 2012

Это linq-to-sql

У меня много разных классов, которые выполняют один и тот же запрос, но проецируют результаты немного по-разному. В идеале я хотел бы иметь возможность разместить запрос в одном месте и передать проекцию в метод Select. Отлично работает для конкретных типов:

public void GetResults() {
    var junk = db.SiteProducts.Select(Project());
}

public Expression<Func<DbEntities.SiteProduct, string>> Project() {
    return p => p.ProductAlert;
}

Но когда я пытаюсь вернуть анонимный тип, происходит сбой

public void GetResults() {
    var junk = db.SiteProducts.Select(Project());
}

public Expression<Func<DbEntities.SiteProduct, TResult>> Project<TResult>() {
    return p => new { p.ProductAlert };
}

Я полностью понимаю, почему вывод общего типа не работает во втором случае. Но есть ли хитрость - если не считать создания собственных выражений с нуля - мне не хватает того, что могло бы заставить это работать?

Ответы [ 4 ]

2 голосов
/ 17 марта 2012

Это интригующий вопрос.Я думаю, что DTO может помочь вам в этом, но есть ограничения и подводные камни, на которые стоит обратить внимание.Возьмите следующий LINQPad Пример:

class ProjectDTO
{
    public string Name { get; set; }

    public static Expression<Func<Project, ProjectDTO>> ToDTO = (e) => new ProjectDTO
    {
        Name = e.Name
    };

    public ProjectDTO() {}

    public ProjectDTO(Project project)
    {
        Name = project.Name;
    }
}

void Main()
{
    Projects.Select(p => p.Name).Dump();
    Projects.Select(ProjectDTO.ToDTO).Dump();
    Projects.Select(p => new ProjectDTO(p)).Dump();
}

Сгенерированный SQL:

SELECT [t0].[Name]
FROM [Project] AS [t0]
GO

SELECT [t0].[Name]
FROM [Project] AS [t0]
GO

SELECT [t0].[ProjectId], [t0].[Name], [t0].[Description], [t0].[DateCreated], [t0].[DateModified], [t0].[DateComplete], [t0].[CreatedBy]
FROM [Project] AS [t0]

Как видите, вы не можете использовать конструктор копирования для назначения свойствDTO, поскольку это заставляет весь объект извлекаться из базы данных.

Это также немного ограничивает, если вы хотите расширить базовый DTO и добавить больше свойств для более специализированных представлений данных, что означает, что вы могли быВ итоге получим несколько Expression с похожим кодом.

Однако мне очень нравится второй вариант, но я уверен, что этот вариант весьма ограничен проекциями одного типа, рассмотрим следующий пример:

var query = from p in Projects
            join t in Tasks on p.ProjectId equals t.ProjectId
            select ProjectDTO.ToDTO; //Can't be used like this

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

Без использования библиотеки Dynamic LINQ или построения дерева выражений вручнуюЯ также хотел бы узнать, возможно ли с помощью LINQ-SQL / LINQ-Entities создавать динамические выборки.

2 голосов
/ 17 марта 2012

Это не будет работать во время компиляции. Используя динамические вещи, вы, конечно, можете заставить их работать.

Простым решением является не использование анонимного типа, а пользовательский класс DTO. Такой класс DTO занимает всего несколько строк и его легко поддерживать. Обычно это хорошее решение.

2 голосов
/ 17 марта 2012

IdeaBlade имеет класс ProjectionSelector, который можно использовать для абстрагирования ваших проекций. Когда вам нужно создать проекционный запрос, но вы не знаете, какие типы задействованы во время компиляции, вы можете создать экземпляр класса ProjectionSelector и передать информацию о типе во время выполнения.

Класс и пример кода можно найти здесь:

Создание динамических предложений «Select», «SelectMany» и «GroupBy»
http://drc.ideablade.com/xwiki/bin/view/Documentation/dynamic-projection

1 голос
/ 17 марта 2012

Если я правильно понимаю ваш вопрос, вы можете использовать этот код:

сначала объявите метод выбора ваших данных следующим образом:

public List<TResult> FindAll<TResult>(Func<Regions, TResult> selector) where TResult : class
    {
        using (RepositoryDataContext = new DataClasses1DataContext())
        {
                return RepositoryDataContext.Regions.Select<Regions, TResult>(selector).ToList<TResult>();

        }
    }

, затем вы можете построить свое утверждение select следующим образом:

Func<Regions, SelectAllRegion> select = r => new SelectAllRegion
        {
            RegionID = r.RegionID,
            RegionDescription = r.RegionDescription
        };

my SelectAllRegion:

 public class SelectAllRegion
{
    public SelectAllRegion()
    {
    }
    public int RegionID { get; set; }
    public string RegionDescription { get; set; }
}

и регион Region стол в северном крыле. Надеюсь, это поможет вам

...