我们使用6.1.2 EF,我们有从数据库生成的edmx。
我们有像POCO这样的课程:
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public long CategoryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
我需要做的是插入具有自定义sql查询的新虚拟属性,如:
public virtual int ProductsCount { get; set; }
将返回select count(1) from dbo.Product where CatgoryID = ***
是否可以像这样定义虚拟财产?我在.edmx xml文件中找到了一些sql查询定义,所以我希望,也可以定义我的。我可以用什么来代替***?
毕竟,我的代码应该被称为简单:
context
.Category
.Include(x => x.ProductsCount)
.ToList();
答案 0 :(得分:1)
与生成的XML混淆在一般情况下不是一个好主意。任何重新生成都会覆盖您的代码。
IMO,您的财产应如下:
public int ProductCount { get { return Products.Count(); } }
您的请求应该是:
context.Category.Include(x => x.Products).ToList();
如果加载完整的产品列表可能会导致问题,我会使用DB View或带有计算列的Category表,然后更新我的EF映射以使这个新实体继承Category实体。
答案 1 :(得分:0)
如果Entity Framework具有公式属性,如NHibernate,这可能是一个可行的选择。但是,使用实体框架,您无法在不破坏持久性无知和生成n + 1个查询的情况下执行此操作。
持久性无知:实体不应该对创建它们的数据层有任何引用。这是一个不应该被打破的重要命题。否则,您会创建一种难以跟踪(和预测)查询执行的时间和位置的情况。此外,它打破了单一责任原则。
n + 1反模式:除了具有公式属性的NHibernate之外,EF无法将嵌入式查询集成到它执行的查询中以从数据库中获取Category
(1 )。每个类别随后将执行其自己的查询(n)。
执行此操作的最佳方法是将数据投影到视图模型(或DTO类,无论哪个名称最适合您):
context.Categories.Select(c => new CategoryModel
{
Name = c.Name,
OtherProperty = c.OtherProperty,
ProductsCount = c.Products.Count()
}
在投影时,您应该始终关注 AutoMapper 可以为您做些什么。如果你有这个CategoryModel
类,你可以定义一次映射(在应用程序启动时)......
Mapper.CreateMap<Catecory,CategoryModel>();
...并像这样使用它:
context.Categories.Project().To<CategoryModel>();
AutoMapper的优点在于它将ProductsCount
之类的属性解析为源类Products
中的属性,以及处理它的操作Count()
。这一行代码与前面的代码片段完全相同。
附注:同样,AutoMapper解析嵌套属性。 CategoryName
类中可能包含的属性ProductModel
(如果产品类别为n-1)将解析为product.Category.Name
。