我正在学习C#。NET和EF(使用aspnetboileplate),我想出了创建一些虚拟项目的想法,这样我就可以练习了。但在过去的4个小时里,我遇到了这个错误,希望这里有人能帮助我
我创建的(好吧,至少我认为我创建它是正确的)是2个名为"的类;成分";以及";"大师";
我想用它来对Ingredient进行分类;"大师";班
例如等成分
- 鸡胸肉
- 鸡腿
它们都属于Meat(在"Master"数据库中输入了witch),这是我的代码
入口.cs
public class Ingrident : Entity
{
public string Name { get; set; }
public Master Master { get; set; }
public int MasterId { get; set; }
}
Master.cs
public class Master : Entity
{
public string Name { get; set; }
public List<Ingrident> Ingridents { get; set; } = new();
}
IngridientAppService.cs
public List<IngridientDto> GetIngWithParent()
{
var result = _ingRepository.GetAllIncluding(x => x.Master);
//Also I try this but doesn`t work
// var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
return ObjectMapper.Map<List<IngridientDto>>(result);
}
入口Dto.cs
[AutoMap(typeof(IndexIngrident.Entities.Ingrident))]
public class IngridientDto : EntityDto
{
public string Name { get; set; }
public List<MasterDto> Master { get; set; }
public int MasterId { get; set; }
}
MasterDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
{
public string Name { get; set; }
}
当我创建(为上次练习)M->M关系这种方法与.getAll包括工作,但现在当我有一个->许多人认为这行不通。
希望有人能帮助我,或者至少给我一些好的提示。
祝你今天愉快!
直接说,您可能引用的示例(关于存储库等)过于复杂,在大多数情况下,不是您想要实现的。
我看到的第一个问题是,虽然你的实体是为从Master到Ingredients的一对多关系而设置的,但你的DTO是从Ingredient到Masters设置的,这肯定不会正确映射。
从最简单的事情开始。去掉存储库,去掉DTO。我不确定基类";实体";是的,但我猜它公开了一个名为"的公共密钥属性;Id";。首先,我可能也会放弃这一点。当涉及到主键时,通常有两种命名方法,每个表都使用称为"主键"的PK;Id";,或者每个表使用一个PK,其中TableName后缀为";Id";。即";Id";与";入口Id";。就我个人而言,我发现第二种选择非常清楚地表明,当FK和PK配对时,它们会有相同的名称。
当涉及到通过导航属性表示关系时,一个重要的细节是确保导航属性链接到它们各自的FK属性(如果存在),或者更好的做法是为FK使用阴影属性。
例如,对于Ingredient表,去掉Entity基类:
[Table("Ingredients")]
public class Ingredient : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public string Name { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public virtual Master Master { get; set; }
}
此示例使用EF属性来帮助告诉EF如何将实体属性解析为相应的表和列,以及Ingredient和Master之间的关系。EF可以按照惯例解决大部分问题,但最好明确理解和应用它,因为最终你会遇到惯例无法发挥作用的情况。
识别(主)密钥并指示其为"身份"列也会告诉EF预计数据库将自动填充PK。(强烈推荐)
在Master方面,我们做了类似的事情:
[Table("Masters")]
public class Master : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MasterId { get; set; }
public string Name { get; set; }
[InverseProperty("Master")]
public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
我们再次表示主键,对于我们的Ingredients集合,我们使用InverseProperty
属性告诉EF它应该使用另一侧(Ingredient)的什么属性来关联到此Master的Ingredient列表。
属性只是设置关系等的一个选项。其他选项是使用实现IEntityConfiguration<TEntity>
(EF Core)的配置类,或将它们配置为DbContext中OnModelCreating
事件的一部分。最后一个选项我只推荐给非常小的项目,因为它可以很快成为一种上帝的方法。您可以将其拆分为对各种私有方法的调用,但也可以只使用IEntityConfiguration
类。
现在,当你去拿它的大师的配料,或者一个大师的配料:
using (var context = new AppDbContext())
{
var ingredients = context.Ingredients
.Include(x => x.Master)
.Where(x => x.Master.Name.Contains("chicken"))
.ToList();
// or
var masters = context.Master
.Include(x => x.Ingredients)
.Where(x => x.Name.Contains("chicken"))
.ToList();
// ...
}
存储库模式是一个更高级的概念,有一些很好的理由来实现,但在大多数情况下,它们是不必要的,并且是EF实现中的反模式。我认为的泛型存储库总是是EF实现的反模式。即Repository<Ingredient>
不使用存储库,尤其是带有EF的通用存储库的主要原因是,您会自动增加实现的复杂性和/或削弱EF为您的解决方案带来的功能。正如您在使用示例时看到的那样,简单地将热切加载传递到存储库意味着写入复杂的Expression<Func<TEntity>>
参数,而这仅涵盖了热切加载。如果没有EF开箱即用提供的这些功能,支持投影、分页、排序等会增加锅炉板的复杂性或限制您的解决方案和性能。
考虑研究存储库实现/w EF:的一些好理由
- 促进单元测试。(存储库比DbContexts/DbSets更容易模拟)
- 集中化低级数据规则,如租用、软删除和授权
考虑存储库的一些不好的(尽管很常见)原因:
- 从引用或EF依赖性知识中抽象代码
- 对代码进行抽象,以便EF可以被替换掉
投影到DTO或ViewModels是使用EF构建高效安全解决方案的一个重要方面。目前尚不清楚";ObjectMapper";是,无论它是Automapper Mapper实例还是其他实例。我强烈建议您使用Linq的Select
语法来填充模型中所需的DTO,从而开始掌握投影。正确使用Projection时的第一个关键区别是,当您投影对象图时,您确实而不需要担心急于加载相关实体。投影(Select
)中引用的任何相关实体/属性都将根据需要自动加载。稍后,如果您想利用像Automapper这样的工具来帮助消除Select
语句的混乱,您将需要配置映射配置,然后使用Automapper的ProjectTo
方法,而不是Map
。ProjectTo
与EF的IQueryable
实现一起工作,以解决映射到SQL的问题,就像Select
一样,其中Map
需要返回所有急切加载的内容,以便填充相关数据。CCD_ 16和CCD_。(数据库和服务器/应用程序之间的连接数据较少)Map
仍然非常有用,例如您希望将值从DTO复制回加载的实体的场景。
喜欢这个吗
public class Ingrident:Entity
{
public string Name { get; set; }
[ForeignKey(nameof(MasterId))]
public Master Master { get; set; }
public int MasterId { get; set; }
}