Entity Framework插入记录后如何正确加载关联实体并避免额外查询?
问题解答
现有方案评估
你当前的实现是可行但非最优的:确实可以成功获取到关联表的完整信息,但会多产生一次针对ContactAddresses表的查询请求,高并发场景下会带来不必要的性能损耗。
插入后导航属性为空的原因
EF 插入数据时只会填充你显式赋值的字段、以及数据库自动生成的字段(比如自增主键Id),不会自动加载关联导航属性。导航属性只有在主动Include查询、手动赋值、或开启懒加载后访问时才会被填充。
避免额外查询的两种方案
方案1:提前查询关联实体并直接赋值
如果你的业务逻辑本来就需要校验传入的ContinentId/CountryId/CountyId是否合法,可以直接在插入前查询对应的关联实体,赋值给导航属性,SaveChanges后导航属性天然有值,没有额外查询。
public async Task<ContactAddress> AddNewContactAddress(AddressViewModel viewModel) { // 提前校验关联数据合法性,同时获取实体 var continent = await context.Continents.FirstOrDefaultAsync(c => c.Id == viewModel.ContinentId); var country = await context.Countries.FirstOrDefaultAsync(c => c.Id == viewModel.CountryId); var county = await context.Counties.FirstOrDefaultAsync(c => c.Id == viewModel.CountyId); // 可在此处添加校验逻辑,关联数据不存在时抛出参数异常 ContactAddress dbModel= new ContactAddress { AddressLine = viewModel.AddressLine, Town = viewModel.Town, PostCode = viewModel.PostCode, Continent = continent, Country = country, County = county }; context.ContactAddresses.Add(dbModel); await context.SaveChangesAsync().ConfigureAwait(false); // 此时导航属性已经有值,无需额外查询 return dbModel; }
方案2:插入后手动加载导航属性
如果已经能保证传入的外键Id合法,不需要提前校验,可以在插入完成后通过上下文的Entry方法手动加载关联导航属性,只会查询对应的关联表,不需要重新查询ContactAddresses表,比你现有方案性能更好。
public async Task<ContactAddress> AddNewContactAddress(AddressViewModel viewModel) { ContactAddress dbModel= new ContactAddress { AddressLine = viewModel.AddressLine, Town = viewModel.Town, PostCode = viewModel.PostCode, ContinentId = viewModel.ContinentId, CountryId = viewModel.CountryId, CountyId = viewModel.CountyId }; context.ContactAddresses.Add(dbModel); await context.SaveChangesAsync().ConfigureAwait(false); // 手动加载三个导航属性,仅查询关联表对应记录 await context.Entry(dbModel).Reference(c => c.Continent).LoadAsync(); await context.Entry(dbModel).Reference(c => c.Country).LoadAsync(); await context.Entry(dbModel).Reference(c => c.County).LoadAsync(); return dbModel; }
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

