Ivan Koshelev
Ivan Koshelev

Reputation: 4280

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, i.e. 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.

Upvotes: 1

Views: 630

Answers (1)

Ivan Koshelev
Ivan Koshelev

Reputation: 4280

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

Upvotes: 1

Related Questions