public async Task<bool> CreateAsync(CreateSampleDto createSampleDto)
{
Sample sample = new()
{
YarnName = createSampleDto.YarnName,
LoopsQuantity = createSampleDto.LoopsQuantity,
RowsQuantity = createSampleDto.RowsQuantity,
NeedleSize = createSampleDto.NeedleSize,
NeedleSizeUnit = createSampleDto.NeedleSizeUnit,
Description = createSampleDto.Description,
User = _userService.GetUserByIdAsync(createSampleDto.UserId)
};
await _databaseContext.Samples.AddAsync(sample);
await _databaseContext.SaveChangesAsync();
return true;
}
我想使用实体框架将新的 Sample 对象添加到数据库中。示例具有名为“用户”的导航属性(用户有许多示例)。我想在代码中仅使用导航属性,但我认为使用这种方法从数据库获取用户可能效率低下。当我创建新的 Sample 对象时,是否有更好的方法来附加数据库中存在的用户?我尝试使用 Attach() 函数,但我根本无法处理它。
这个方法非常标准。通过 ID 获取用户应该是一个相对快速的操作,它还可以验证用户是否存在,并且可以强制执行任何较低级别的规则。例如,如果您正在构建一个多租户 SAAS 系统,其中来自不同租户的不同用户位于同一数据库中,您的服务将具有代码来确保为当前登录会话检索的数据仅相关,即获取其注册的租户 ID。如果您的代码仅设置 FK,则可能存在将无效 FK 分配给记录的风险。 (即存在的记录的 FK,但不应应用于此用户/场景)
如果不存在分配不正确 FK 的潜在问题,另一种方法是将存根附加到 DbContext 来表示现有记录。但是,特别是在使用注入的 DbContext 和可能属于较大工作链的操作时,您应始终在附加实体之前检查 DbContext 跟踪缓存。如果不这样做,可能会导致上下文已经在跟踪匹配实体的情况异常。
bool usedStub = false;
var user = _context.Users.Local
.FirstOrDefault(x => x.UserId == createSampleDto.UserId);
if (user == null)
{
user = new User { UserId = createSampleDto.UserId };
_context.Attach(user);
usedStub = true;
}
Sample sample = new()
{
YarnName = createSampleDto.YarnName,
LoopsQuantity = createSampleDto.LoopsQuantity,
RowsQuantity = createSampleDto.RowsQuantity,
NeedleSize = createSampleDto.NeedleSize,
NeedleSizeUnit = createSampleDto.NeedleSizeUnit,
Description = createSampleDto.Description,
User = user
};
_context.Samples.Add(sample);
await _context.SaveChangesAsync();
// Remove the stub
if (usedStub)
_context.Entry(user).State = EntityState.Detached;
前提是检查用户的本地跟踪缓存,如果找到,我们将使用它。此检查不会进入数据库。如果尚未跟踪它,那么我们可以创建存根并附加它。由于我们附加了一个仅包含 UserID 的存根,因此我们不希望 DbContext 在稍后可能希望在请求中加载实际用户的情况下返回该存根,因此如果我们添加了一个存根,那么我们会分离该存根一次我们完成了。
现在将其应用到现有的 UserService 模式中可能不会那么简单。这是抽象 DbContext 可能出现问题的原因之一,因此像您当前所做的那样使用 UserService 加载用户可能更安全。