在EF中使用扩展方法投射单个实体 [英] Projection of single entities in EF with extension methods
问题描述
我想要做一些像下面这样的事情:
ctx.People.FiltersAndThings()。ToViewModels(); //项目我的DB模型进入视图模型
实际投影的扩展方法
code> public static IQueryable< PersonModel> ToViewModels(此IQueryable< Person>实体)
{
return entities.Select(x => new PersonModel {
Me = x.Me.ToViewModel(),//此方法不能翻译成一个商店表达式
Friends = x.Friends.AsQueryable()。ToViewModels()//适用于一些魔术(tm)
});
}
public static IQueryable< ProfileModel> ToViewModels(这个IQueryable< Profile>实体)
{
return entities.Select(x => new ProfileModel {Name = x.Name});
}
public static ProfileModel ToViewModel(此配置文件实体)
{
返回新的ProfileModel {Name = entity.Name};
}
使用Queryable时(例如 Friends = x。 Friends.AsQueryable()。ToViewModels()
)我们可以使用一些魔术来将其压缩成一个表达式(请参阅 https://stackoverflow.com/a/10726256/1070291 ,@LordTerabyte的答案)但是当我们使用新的子句(例如 Me = new ProfileModel {Name = x .Me.Name}
)它不是一个表达式,所以如果我们在扩展方法下捆绑它(例如 Me = x.Me.ToViewModel()
)我们不能把这个表达出来。
在EF中,新对象的作业如何在幕后工作?
有没有办法通过扩展方法转换为新对象?
这里的完整演示代码: https://github.com/lukemcgregor/ExtensionMethodProjection
编辑
我现在有一个博文(可组合的存储库 - 嵌套扩展)和 nuget包帮助嵌套扩展方法在linq中
看看这个答案。它做一个非常相似的事情你想要什么。基本上你会将转换定义为一个表达式树,例如:
public static Expression&FunC< Profile,ProfileModel>> ToProfileViewModel()
{
return entity =>新的ProfileModel {Name = entity.Name};
}
然后调用它(例如ExpressionsHelper.ToProfileViewModel())AsQuote )(p))。
如果你愿意,你可以修改访问者,以允许更好的语法。一些沿着线:
[ReplacementInExpressionTrees(MethodName = nameof(ExpressionsHelper.ToProfileViewModel))]
public static ProfileModel ToViewModel (这个配置文件)
{
//这个实现只在这里,所以如果你在非表达式树中调用方法,它仍然可以工作
返回ExpressionsHelper.ToProfileViewModel()。编译()(简档); //提示:缓存编译的func!
现在,您需要创建一个访问者,检查所有方法调用,当找到一个方法时属性,它将整个调用更改为ExpressionsHelper.ToProfileViewModel()。AsQuote()(配置文件)。这是你的一个练习:)
}
I like to do projection from my entity models into my view models using extension methods. This means I dont over/under fetch for my models and it makes the code nice and readable. It makes sense that sometimes the projections may include nested models, and I want to get reuse on those sub-projections.
I want to be able to do something like the following:
ctx.People.FiltersAndThings().ToViewModels();//the project my DB Models into view models
Extension methods for actual projection
public static IQueryable<PersonModel> ToViewModels(this IQueryable<Person> entities)
{
return entities.Select(x => new PersonModel {
Me = x.Me.ToViewModel(), //this method cannot be translated into a store expression
Friends = x.Friends.AsQueryable().ToViewModels() //works fine with some magic (tm)
});
}
public static IQueryable<ProfileModel> ToViewModels(this IQueryable<Profile> entities)
{
return entities.Select(x => new ProfileModel { Name = x.Name });
}
public static ProfileModel ToViewModel(this Profile entity)
{
return new ProfileModel { Name = entity.Name };
}
When using a Queryable (eg Friends = x.Friends.AsQueryable().ToViewModels()
) we can use some magic to flatten this to an expression (see https://stackoverflow.com/a/10726256/1070291, answer by @LordTerabyte) But when we are doing an assignment with a new clause (eg Me = new ProfileModel { Name = x.Me.Name }
) its not an expression so if we bundle this under an extension method (eg Me = x.Me.ToViewModel()
) we can't flatten this to an expression.
How does an assignment to a new object work under the scenes in EF?
Is there a way to do convertion to a new object via an extension method?
Full demo code here: https://github.com/lukemcgregor/ExtensionMethodProjection
Edit:
I now have a blog post (Composable Repositories - Nesting Extensions) and nuget package to help with nesting extension methods in linq
Take a look at this answer. It does a very similar thing what you want. Basically you would define your transformation as an Expression tree, e.g:
public static Expression<Func<Profile, ProfileModel>> ToProfileViewModel()
{
return entity => new ProfileModel { Name = entity.Name };
}
And then do invocations of this (e.g. ExpressionsHelper.ToProfileViewModel().AsQuote()(p)).
If you prefer, you can modify the visitors, to allow a nicer syntax. Something along the lines:
[ReplacementInExpressionTrees(MethodName=nameof(ExpressionsHelper.ToProfileViewModel))]
public static ProfileModel ToViewModel(this Profile profile)
{
// this implementation is only here, so that if you call the method in a non expression tree, it will still work
return ExpressionsHelper.ToProfileViewModel().Compile()(profile); // tip: cache the compiled func!
Now you need to create a visitor, that checks all method calls, and when finds a method with this attribute, it changes the whole call to ExpressionsHelper.ToProfileViewModel().AsQuote()(profile). This is as an exercise for you :) }
这篇关于在EF中使用扩展方法投射单个实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!