Предайте израз към Select на linq

Това е 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 };
}

Напълно разбирам защо изводът за общ тип се проваля във втория случай. Но има ли трик – освен да създавам мои собствени изрази от самото начало – пропускам, който би могъл да накара това да работи?


person Adam Rackis    schedule 16.03.2012    source източник
comment
Нещо като това? stackoverflow.com/questions/1299534   -  person Robert Harvey    schedule 17.03.2012
comment
@Robert - този пример изглежда се опитва да напише заместител на Where, което е малко по-различно. Но може да пропускам нещо. Благодаря ви все пак.   -  person Adam Rackis    schedule 17.03.2012
comment
Има и това: stackoverflow.com/questions/4683427, което може да е малко по-близо до това, което се опитвате да направите.   -  person Robert Harvey    schedule 17.03.2012
comment
drc.ideablade.com/xwiki/bin/view/Documentation/   -  person Robert Harvey    schedule 17.03.2012
comment
@Робърт - последната връзка е на място - можеш ли да направиш това отговор?   -  person Adam Rackis    schedule 17.03.2012


Отговори (4)


Ако разбирам правилно въпроса ви, можете да използвате този код:

първо декларирайте метод за избор на вашите данни по следния начин:

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 таблица в Northwing. Надявам се това да ви помогне

person Arian    schedule 17.03.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 и да добавите повече свойства за по-специализирани изгледи на данните, което означава, че можете да получите множество изрази с подобен код.

Въпреки това, аз доста харесвам вариант две, но съм сигурен, че тази опция е доста вероятно ограничена до проекции от един тип, разгледайте следния пример:

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 да създавам динамични селекции.

person Hux    schedule 17.03.2012

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

Класът и примерният код могат да бъдат намерени тук:

Създаване на динамични клаузи „Select“, „SelectMany“ и „GroupBy“
http://drc.ideablade.com/xwiki/bin/view/Documentation/dynamic-projection

person Robert Harvey    schedule 16.03.2012
comment
Благодаря Робърт. Съществуването на този проект ме кара да вярвам, че няма синтактична захар, която мога да поръся, за да накарам това да работи както е, но ще го оставя още малко отворен за всеки случай. - person Adam Rackis; 17.03.2012
comment
Те може да излъчват IL под капака. Ето как те карат техния клас от анонимен тип да работи по време на изпълнение. - person Robert Harvey; 17.03.2012
comment
Има и този очарователен код: msdn.microsoft.com/en-us /vstudio/bb737920#dynsel - person Robert Harvey; 17.03.2012
comment
Разбира се - този msdn го свързва, просто изгражда дърво на изрази ръчно. Вероятно това, което прави проектът IdeaBlade. - person Adam Rackis; 17.03.2012

Това няма да работи по време на компилиране. Използвайки динамични неща, можете да го накарате да работи, разбира се.

Едно просто решение е да не използвате анонимен тип, а специално създаден DTO клас. Такъв DTO клас отнема само много малко редове и е лесен за поддръжка. Обикновено това е добро решение.

person usr    schedule 16.03.2012