【发布时间】:2018-02-14 12:38:00
【问题描述】:
我在此站点上引用了许多与计算字段和 ViewModel 相关的问题,但我似乎无法从给出的示例中推断出来。我希望布置一个特定的场景可以让某人指出我看不到的东西。一般来说,我是 WebApp 设计的新手。请考虑到这一点。另外,如果我遗漏了任何相关信息,请告诉我,我会更新问题。
这里是场景:
我有一个复杂的查询,它跨越多个表以返回计算中使用的数据。具体来说,我存储转换为基本单位的配方的单位,然后将数量转换为用户指定的单位。
我正在使用 AutoMapper 从实体映射到 ViewModel,反之亦然,但我不确定如何处理计算值。尤其是嵌套的 ViewModel 集合。
选项 1
我是否返回一组自主的数据?像下面这样......然后以某种方式使用 AutoMapper 进行映射?也许我需要手动进行映射,我还没有找到一个包含嵌套 ViewModel 的可靠示例。在这一点上,我什至不确定以下代码是否正确处理了自治数据的嵌套集合。
var userId = User.Identity.GetUserId();
var recipes = from u in db.Users.Where(u => u.Id == userId)
from c in db.Categories
from r in db.Recipes
join ur in db.UserRecipes.Where(u => u.UserId == userId) on r.Id equals ur.RecipeId
join mus in db.MeasUnitSystems on ur.RecipeYieldUnitSysId equals mus.Id
join muc in db.MeasUnitConvs on mus.Id equals muc.UnitSysId
join mu in db.MeasUnits on mus.UnitId equals mu.Id
join msy in db.MeasUnitSymbols on mu.Id equals msy.UnitId
select new
{
Id = c.Id,
ParentId = c.ParentId,
Name = c.Name,
Descr = c.Descr,
Category1 = c.Category1,
Category2 = c.Category2,
Recipes = new
{
Id = r.Id,
Title = r.Title,
Descr = r.Descr,
Yield = String.Format("{0} {1}", ((r.Yield * muc.UnitBaseConvDiv / muc.UnitBaseConvMult) - muc.UnitBaseConvOffset), msy.Symbol)
}
};
选项 2
我想到的另一个选项是返回实体并像往常一样使用 AutoMapper。然后遍历集合并在那里执行计算。我觉得我可以完成这项工作,但对我来说似乎效率低下,因为它会导致许多查询返回数据库。
选项 3
????我想不出任何其他方法来做到这一点。但是,如果您有任何建议,我非常愿意倾听。
相关数据
这是在 SQL Server 中返回我想要的数据的查询(或多或少)。
declare @uid as nvarchar(128) = 'da5435ae-5198-4690-b502-ea3723a9b217'
SELECT c.[Name] as [Category]
,r.Title
,r.Descr
,(r.Yield*rmuc.UnitBaseConvDiv/rmuc.UnitBaseConvMult)-rmuc.UnitBaseConvOffset as [Yield]
,rmsy.Symbol
FROM Category as c
inner join RecipeCat as rc on c.Id = rc.CategoryId
inner join Recipe as r on rc.RecipeId = r.Id
inner join UserRecipe as ur on r.Id = ur.RecipeId and ur.UserId = @uid
inner join MeasUnitSystem as rmus on ur.RecipeYieldUnitSysId = rmus.Id
inner join MeasUnitConv as rmuc on rmus.Id = rmuc.UnitSysId
inner join MeasUnit as rmu on rmus.UnitId = rmu.Id
inner join MeasUnitSymbol as rmsy on rmu.Id = rmsy.UnitId
inner join UserUnitSymbol as ruus on rmsy.UnitId = ruus.UnitId and rmsy.SymIndex = ruus.UnitSymIndex and ruus.UserId = @uid
视图模型
public class CategoryRecipeIndexViewModel
{
public int Id { get; set; }
public int ParentId { get; set; }
[Display(Name = "Category")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
public ICollection<CategoryRecipeIndexViewModel> Category1 { get; set; }
public CategoryRecipeIndexViewModel Category2 { get; set; }
public ICollection<RecipeIndexViewModel> Recipes { get; set; }
}
public class RecipeIndexViewModel
{
public int Id { get; set; }
[Display(Name = "Recipe")]
public string Title { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
[Display(Name = "YieldUnit")]
public string Yield { get; set; }
}
2018 年 2 月 10 日更新
我找到了一个答案here,它很好地解释了我在看什么。特别是在 更好的解决方案? 部分下。将查询直接映射到我的 ViewModel 看起来也可以让我得到我的计算值。问题是,给出的例子又过于简单了。
他给出了以下 DTO 的
public class UserDto
{
public int Id {get;set;}
public string Name {get;set;}
public UserTypeDto UserType { set; get; }
}
public class UserTypeDto
{
public int Id { set; get; }
public string Name { set; get; }
}
并为映射执行以下操作:
var users = dbContext.Users.Select(s => new UserDto
{
Id = s.Id,
Name = s.Name,
UserType = new UserTypeDto
{
Id = s.UserType.Id,
Name = s.UserType.Name
}
});
如果 UserDTO 看起来像这样会怎样:
public class UserDto
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<UserTypeDto> UserTypes { set; get; }
}
如果 UserTypes 是一个集合,映射将如何完成?
2018 年 2 月 13 日更新
我觉得我正在取得进步,但目前正朝着错误的方向前进。我找到了this 并提出了以下内容(由于 linq 查询中的方法调用,目前出现错误):
*注意:我从 ViewModel 中删除了 Category2,因为我发现它不是必需的,只会进一步复杂化。
在索引控制器方法中查询
IEnumerable<CategoryRecipeIndexViewModel> recipesVM = db.Categories
.Where(x => x.ParentId == null)
.Select(x => new CategoryRecipeIndexViewModel()
{
Id = x.Id,
ParentId = x.ParentId,
Name = x.Name,
Descr = x.Descr,
Category1 = MapCategoryRecipeIndexViewModelChildren(x.Category1),
Recipes = x.Recipes.Select(y => new RecipeIndexViewModel()
{
Id = y.Id,
Title = y.Title,
Descr = y.Descr
})
});
递归方法
private static IEnumerable<CategoryRecipeIndexViewModel> MapCategoryRecipeIndexViewModelChildren(ICollection<Category> categories)
{
return categories
.Select(c => new CategoryRecipeIndexViewModel
{
Id = c.Id,
ParentId = c.ParentId,
Name = c.Name,
Descr = c.Descr,
Category1 = MapCategoryRecipeIndexViewModelChildren(c.Category1),
Recipes = c.Recipes.Select(r => new RecipeIndexViewModel()
{
Id = r.Id,
Title = r.Title,
Descr = r.Descr
})
});
}
在这一点上,我什至没有我需要的计算,但这并不重要,直到我得到这个工作(小步骤)。我很快发现你不能真正调用 Linq 查询中的方法。然后我想到,如果我需要强制执行 Linq 查询,然后对内存中的数据执行所有映射,那么我基本上会做与 Option 2 相同的事情(以上),但我可以在 ViewModel 中执行计算。这是我将追求的解决方案,并将让每个人都知道。
【问题讨论】:
-
收藏有什么问题? AutoMapper 对集合属性没有任何问题,只要 1) 名称匹配或名称映射已明确配置,并且 2) 子类有映射配置。而且w.r.t。计算:这些也可以在 AM 中使用 ForMember 进行配置。
-
我对集合的唯一问题是,如果我自己进行映射以获得我想要的数据和计算,但 aakash 回答了这个问题,我相信这是错误的方向我的类别表的递归性质。我已经成功地将 AutoMapper 与集合属性一起使用,但在涉及计算时不确定如何使用它。我目前的计划是将 EF 与 AM 一起使用,以带回所有数据,包括计算所需的数据,然后在 ViewModels 中进行计算。请参阅 2/13 更新。
标签: c# asp.net-mvc-5 entity-framework-6 ef-database-first