Ivan Koshelew Ivan Koshelew - 1 month ago 5
C# Question

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.

Answer

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

Comments