c# - 阻止 EF 添加或更新某些子实体
问题描述
tl;博士;
在实体框架 6 中是否可以指示 EF 不添加或更改某些实体,而是Attach
在保存时将它们分配给父级时隐含它们?
背景
我正在使用 AutoMapper 映射到 DTO 和从 DTO 映射。我有一个这样的模型:
赞助
- 赞助商编号
- 实体 ID
- (其他领域)...
实体
- 实体 ID
- 地址(地址收集)
- (其他领域)...
地址
- 地址 ID
- ...(其他领域)...
- 状态标识
- 状态
- 国家 ID
- 国家
- ...(其他领域)
国家
- 国家 ID
- 国家代码
- 国家的名字
状态
- 状态标识
- 州代码
- 州名
我将以下 JSON 正文作为 DTO 发送到 Web API,并使用 AutoMapper 转换为实体(带子实体):
{
... other stuff ...
"addresses": [
{
"countryId": 1,
"stateId": 34,
"country": {
"countryId": 1,
"countryCode": "USA",
"countryName": "United States"
},
"state": {
"stateId": 34,
"stateCode": "NV",
"stateName": "Nevada"
}
}
]
}
当我将 JSON/DTO 映射到我的 EF 模型并逐步执行代码时,一切似乎都很好——ID 是正确的。但是,当我在上下文中调用 SaveChanges() 时,它会添加重复的州和国家/地区。数据库中的 StateId/CountryId 字段是主键,并且在 EF 中以这种方式指定。
如果我做这样的事情,它会起作用(只要我在同一个国家/州没有两个地址):
foreach (var address in sponsor.Entity.Addresses)
{
address.State = db.Attach(address.State);
address.Country = db.Attach(address.Country);
}
如果我确实有两个具有相同州或国家/地区的地址,它将再次迭代并再次调用该“附加”代码并引发异常。
所以相反,我建立了一个缓存机制:
Dictionary<string, State> _stateCache = new Dictionary<string, State>();
...
foreach (var address in sponsor.Entity.Addresses)
{
if (!_stateCache.ContainsKey(address.State.StateCode))
_stateCache.Add(address.State.StateCode, db.Attach(address.State));
address.State = _stateCache[address.State.StateCode];
// (... plus identical logic for countries ...)
}
这是很多(看似)不必要的管道,我将不得不为这些表和许多其他表做。有没有更简单的方法?还是我在这里做错了什么导致这种行为?
我只想简单地说:“州和国家/地区(以及其他十几个)-当分配为子属性时,当 EF 保存您的父记录时隐式附加您自己-不要创建新记录,您是双亲的儿子。 !”
我猜基本上是只读模式,对于实体列表......
万一这还不够阅读
这是实际的映射
CreateMap<SponsorDto, Entity>()
.ForMember(x => x.EntityId, y => y.MapFrom(z => z.EntityId))
.ForMember(x => x.Addresses, y => y.MapFrom(z => z.Addresses))
.ForMember(x => x.Name, y => y.MapFrom(z => z.Name));
CreateMap<SponsorDto, Sponsor>()
.ForMember(x => x.SponsorId, y => y.MapFrom(z => z.EntityId))
.ForMember(x => x.Entity, y => y.MapFrom(z => z))
.ReverseMap();
CreateMap<Sponsor, SponsorDto>()
.ForMember(x => x.EntityId, y => y.MapFrom(z => z.SponsorId))
.ForMember(x => x.Addresses, y => y.MapFrom(z => z.Entity.Addresses))
.ForMember(x => x.Name, y => y.MapFrom(z => z.Entity.Name))
CreateMap<Address, AddressDto>()
.ForMember(x => x.CodeState, y => y.MapFrom(z => z.CodeState))
.ForMember(x => x.StateId, y => y.MapFrom(z => z.CodeState.StateId))
.ForMember(x => x.CodeCountry, y => y.MapFrom(z => z.CodeCountry))
.ForMember(x => x.CountryId, y => y.MapFrom(z => z.CodeCountry.CountryId))
.EqualityComparison((x, y) => x.AddressId == y.AddressId); // from automapper.collections
CreateMap<AddressDto, Address>()
.ForMember(x => x.CodeState, y => y.MapFrom(z => z.CodeState))
.ForMember(x => x.CodeCountry, y => y.MapFrom(z => z.CodeCountry))
.EqualityComparison((x, y) => x.AddressId == y.AddressId);
地址的 EF 配置
public AddressConfiguration(string schema)
{
ToTable("Address", schema);
HasKey(x => x.AddressId);
Property(x => x.AddressId).HasColumnName(@"AddressId").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.CountryId).HasColumnName(@"CountryId").HasColumnType("int").IsRequired();
Property(x => x.StateId).HasColumnName(@"StateId").HasColumnType("int").IsOptional();
// Foreign keys
HasOptional(a => a.CodeState).WithMany().HasForeignKey(c => c.StateId).WillCascadeOnDelete(false); // FK_Address_State
HasRequired(a => a.CodeCountry).WithMany().HasForeignKey(c => c.CountryId).WillCascadeOnDelete(false); // FK_Address_Country
}
谢谢
解决方案
推荐阅读
- docker - Docker 和 RUN sed 字符串转义
- java - 如何使按钮对用户独占?
- r - 寻找全局最小值
- csv - 尝试使用 tensorflow 数据集为 keras 模型准备 CSV
- node.js - 如何在 Google Cloud Storage 中使用 bucket.upload() 而不是 file.createWriteStream()?
- python - 覆盖子类中的属性设置器抽象方法
- java - 使用 Java 从方法中转换为货币
- java - 如何在 reactJS 的 get 请求中设置参数
- javascript - 如何修复我的 PHP 表单重定向以将文件上传到服务器
- python - 仅使用元组交换浮点值