简体   繁体   中英

Entity Framework Linq set property of an object without additional projection by SELECT

I'm implementing an inheritance scenario with Entity Framework 6. Inheritance only exists on DTO level, ie i have two classes Foo and Bar : Foo , I have first method that selects an IQueryable<Foo> and then several methods that select additional properties for specific inheriting classes like Bar.

Normally, I would have code like

from foo in SelectFoo()
join barAdditionalProps in .....    
select new Bar{
    Id = foo.Id,
    Description = foo.Description,
    Baz = barAdditionalProps.Baz}

which would give a nice single SQL query as a result.

This, unfortunately, means that all properties from foo will have to be copied during second projection (first one is inside SelectFoo ). In real life code that would mean 20+ properties copied in every method using SelectFoo .

I would like to do something like this (code is prepared in LINQPad, assume this == EFContext):

void Main()
{      
    (from barBase in SelectT<Bar>()
     join field in this.Fields on barBase.Id equals field.ProductId
     let _1 = barBase.Baz = field.Baz // this part fails with exception
                                      // An expression tree may not contain an assigment operator         
     select barBase)
    .First()
    .Dump();      
}

public IQueryable<T> SelectT<T>() where T : Foo, new() 
{
    return this
           .Products
           .Select(x => new T
           {
                 Id = x.Id,
                 Description = x.Description
           });
}

public class Foo
{
    public string Description {get;set;}
    public int Id {get;set;}
}

public class Bar : Foo
{
    public int Baz {get;set;}
}

Receiving the exception described above, I'm looking for a way to make this work or any other solution that would allow me not to copy all base class properties during second projection.

Since no existing tools were up to the job, I wrote my own library that uses expression tree modification to project baseclass dto in subclass dto automatically.

Now instead of this

     IQueryable<BaseDto> baseQuery = GetBaseQuery();

  IQueryable<SubclassDto> query = from baseDto in baseQuery
                                  let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id)
                                  select new SubclassDto()
                                  {
                                    NewProp1 = moreData.Foo,
                                    NewProp2 = moreData.Baz,
                                    OldProp1 = moreData.SomeOverridingData,

                                    OldProp2 = baseDto.OldProp2,
                                    OldProp3 = baseDto.OldProp3,
                                    OldProp4 = baseDto.OldProp4,
                                    //... 20 more projections from BaseDto to SubclassDto
                                  };

We have this

 IQueryable<BaseDto> baseQuery = GetBaseQuery();

 IQueryable<SubclassDto> query = from baseDto in baseQuery                                  
                                 let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id) 
                                 select baseDto.AutoProjectInto(() => new SubclassDto()
                                 {
                                  NewProp1 = moreData.Foo,
                                  NewProp2 = moreData.Baz,
                                  OldProp1 = moreData.SomeOverridingData
                                 });

 IQueryable<SubclassDto> activateQuery = query.ActivateAutoProjects(); 

And all properties that were not bound by the SubclassDto initialization are projected from baseDto automatically.

Library is available via Github https://github.com/IKoshelev/Linq.AutoProject and NuGet https://www.nuget.org/packages/Linq.AutoProject

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