tl; dr;
在Entity Framework 6中是否可以指示EF不要添加或更改某些实体,而是在保存时分配给父级时隐式Attach
?
背景
我正在使用AutoMapper映射到DTO。我有一个这样的模型:
赞助人
实体
地址
国家
状态
我将以下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()时,它将添加重复的State和Country。数据库中的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保存您的父记录时隐式地附加您自己-不要创建一个新的记录,您是bi的儿子..枪!”
我猜,对于实体列表,基本上是只读模式...
如果阅读量不足
这是实际的映射
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
}
谢谢