好的,除了标准的“不要编写存储库,EF的
DbSet
这已经是一个存储库“免责声明,你做的事情非常非常错误,我真的很好奇你在哪里遇到过这样的例子??
首先,这段代码需要:
public async Task<Department> GetByIdAsync(string id)
{
var dep = new Department();
dep.DepartmentId = id;
dep.DepartmentName = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.DepartmentName).FirstOrDefaultAsync();
dep.Persons = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(p => p.Persons).FirstOrDefaultAsync();
dep.Users = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.Users).FirstOrDefaultAsync();
dep.DayDepartments = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.DayDepartments).FirstOrDefaultAsync();
dep.Group = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.Group).FirstOrDefaultAsync();
dep.Days = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.Days).FirstOrDefaultAsync();
dep.Months = await _context.Departments.AsNoTracking().Where(d => d.DepartmentId == id).Select(d => d.Months).FirstOrDefaultAsync();
return dep;
}
这有很多问题。首先,它正在制造
7.
调用数据库进行填充
1.
部门。其次,如果找不到部门,它将返回具有提供ID的部门,而不会返回其他任何内容。
这是你应该返回的:
public async Task<Department> GetByIdAsync(string id)
{
var department = await _context.Departments
.AsNoTracking()
.Single(d => d.DepartmentId == id);
return department;
}
如果有要包含的关联实体,则通过添加来快速加载这些实体
.Include(x => x.{Insert Related Navigation Property})
。这会获取一个分离的实体,但如果你获取的是一个意图更新它的实体,那么我会删除
.AsNoTracking()
因此,可以修改返回的实体,并且更改跟踪器将在以下情况下构建合适的SQL
SaveChanges
它被调用到Contoso上。A.
很多
试图包裹EF会引起混乱和并发症
DbContext
/
DbSet
Repository类中的功能,因此我强烈建议删除该存储库,并首先使用
➤
和部门
DbSet
在试图抽象任何东西之前。
如果出于任何原因,您想在读取要穿梭的实体时排除字段等,那么您可以使用单个
.Select()
填充新模型以传递回去。如果您需要填充Department DTO/ViewModel,我不建议使用Department实体:
public async Task<DepartmentViewModel> GetByIdAsync(string id)
{
var departmentVM = await _context.Departments
.Where(d => d.DepartmentId == id)
.Select(d => new DepartmentViewModel
{
DepartmentId = d.DepartmentId,
// .. populate remaining fields...
}).Single();
return departmentVM;
}
不要部分填充部门实体作为视图模型或数据传输DTO结果。这样做的问题是,期望部门实体的方法应该始终期望一个适当的、被跟踪的实体,而不是冒着得到一些未被跟踪的、部分填充的占位符的风险。
错误的症结在于:EF被设计为与引用一起工作。当您创建与其他实体相关的实体时,有两种主要类型的引用。协会和儿童。关联是此实体与数据库中其他现有记录之间的链接。孩子是基本上会根据父母而生或死的物品。类似“组”的东西很可能是一种关联关系,这意味着当你添加或更新一个部门并将其与一个组相关联时,你需要确保EF使用的是它所知道的现有记录,而不是它将视为新组的东西。
因此,例如,如果你想创建一个与ID为10的组记录关联的部门,你不需要执行以下操作:
department.Group = new Group { GroupId = 10; }
或者甚至:
var group = _context.Groups.AsNoTracking().Single(g => g.GroupId == 10);
department.Group = group;
相反,您使用:
var group = _context.Groups.Single(g => g.GroupId == 10);
department.Group = group;
关键区别在于,最后一个示例获取了对组的跟踪引用,其中
AsNoTracking()
将返回一个未跟踪的新实例。如果加入该部门,EF将把其视为“新”组,而不是与现有行的关联。
如果您已经有一个组引用,该引用可能会被跟踪/附加,也可能不会被跟踪/附着,那么如果Contoso碰巧已经在跟踪该组,您需要检查并替换该引用:
var trackedGroup = _context.Groups.Local.FirstOrDefault(g => g.GroupId = group.GroupId);
if (trackedGroup == null)
{
_context.Attach(group);
trackedGroup = group;
}
department.Group = trackedGroup;
这检查
.Local
它只搜索本地跟踪缓存,不会访问数据库。如果我们没有找到被跟踪的副本,我们将跟踪分离的副本(组)。通过这种方式,该部门与被跟踪的参考相关联。
其他需要修复的项目是删除任何代码设置集合导航属性。例如:
public List<Person> Persons { get; set; }
用途:
public List<Person> Persons { get; protected set; } = new [];
保护设置器,删除任何试图设置它的代码。人员列表将被初始化,并准备好进入您创建的任何新部门。代码永远不应该在集合导航属性上调用setter,因为这会破坏EF中的更改跟踪。如果你想替换实体中的一个集合,这并不像转储集合并重新创建它那么简单,你需要弄清楚要删除和添加哪些项目,以便变更跟踪器知道删除要删除的行并插入要添加的行。设置器应仅在单个对象引用上可用,例如
public Group Group { get; set; }
希望这能帮助您在插入和更新数据时开始跟踪正确关联实体的问题。可能还有更多的领域会导致问题,但请尝试这些更改,并逐步减少您试图测试和测试的代码量,以便更快地发现问题。