我有3张桌子,
1. AttributeTypes(列:AttributeId(PK),AttributeName,..)
2.位置(列:locationId(PK),LocationName,…)
3. LocationAttributeType(列:locationId(FK),AttributeId(FK))
每当我尝试从GUI插入新的位置记录及其属性类型时,它应该为Table-Location和LocationAttributeType创建新记录.但EF试图在Table- AttributeTypes中添加新记录,它只是用作参考表,不应在其中添加新的/重复的记录.我怎么能防止这种情况?
这是我的代码,
GUI发送的模型是,
public class LocationDataModel
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Code { get; set; }
[DataMember]
public List<AttributeTypeDataModel> AssignedAttributes = new List<AttributeTypeDataModel>();
}
public class AttributeTypeDataModel
{
protected AttributeTypeDataModel() {}
public AttributeTypeDataModel(int id) { this.Id = id; }
public AttributeTypeDataModel(int id, string name)
: this(id)
{
this.Name = name;
}
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual ICollection<LocationDataModel> Locations { get; set; }
}
由EF创建的实体是,
public partial class Location
{
public Location()
{
this.AttributeTypes = new List<AttributeType>();
}
public Location(int campusId, string code)
: this()
{
CampusId = campusId; Code = code;
}
public int Id { get; set; }
public int CampusId { get; set; }
public string Code { get; set; }
public virtual ICollection<AttributeType> AttributeTypes { get; set; }
}
public partial class AttributeType
{
public AttributeType()
{
this.Locations = new List<Location>();
}
public int AttributeTypeId { get; set; }
public string AttributeTypeName { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
我有以下代码将这些新位置添加到数据库,
private IEnumerable<TEntity> AddEntities<TModel, TEntity, TIdentityType>
(IEnumerable<TModel> models, Func<TModel, TIdentityType> primaryKey,
IGenericRepository<TEntity, TIdentityType> repository)
{
var results = new List<TEntity>();
foreach (var model in models)
{
var merged = _mapper.Map<TModel, TEntity>(model);
var entity = repository.Upsert(merged);
results.Add(entity);
}
repository.Save();
return results.AsEnumerable();
}
我正在使用以下通用存储库来执行与实体相关的操作
public TEntity Upsert(TEntity entity)
{
if (Equals(PrimaryKey.Invoke(entity), default(TId)))
{
// New entity
return Context.Set<TEntity>().Add(entity);
}
else
{
// Existing entity
Context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
public void Save()
{
Context.SaveChanges();
}
我在做什么错了?
最佳答案 DbSet< T> .Add()方法附加了整个对象图.您需要向EF指出“参考”实体实际上已存在.有两种简单的方法可以做到这一点:
>不要将导航属性设置为对象.相反,只需将相应的外键属性设置为正确的值即可.
>您需要确保不将同一实体的多个实例加载到对象上下文中.创建上下文后,将完整的AttributeType实体列表加载到上下文中,并创建一个Dictionary<>存储它们.如果要向Location添加属性,请从字典中检索相应的属性.在调用SaveChanges()之前,遍历字典并将每个AttributeType标记为未更改.像这样的东西:
using (MyContext c = new MyContext())
{
c.AttributeTypes.Add(new AttributeType { AttributeTypeName = "Fish", AttributeTypeId = 1 });
c.AttributeTypes.Add(new AttributeType { AttributeTypeName = "Face", AttributeTypeId = 2 });
c.SaveChanges();
}
using (MyContext c = new MyContext())
{
Dictionary<int, AttributeType> dictionary = new Dictionary<int, AttributeType>();
foreach (var t in c.AttributeTypes)
{
dictionary[t.AttributeTypeId] = t;
}
Location l1 = new Location(1, "Location1") { AttributeTypes = { dictionary[1], dictionary[2] } };
Location l2 = new Location(2, "Location2") { AttributeTypes = { dictionary[1] } };
// Because the LocationType is already attached to the context, it doesn't get re-added.
c.Locations.Add(l1);
c.Locations.Add(l2);
c.SaveChanges();
}
在这种特定情况下,您使用多对多关系,EF自动处理中间表.这意味着您实际上没有在模型中公开FK属性,我上面的第一个建议将不起作用.
因此,您需要使用仍然应该工作的第二个建议,或者您需要放弃中间表的自动处理,而是为它创建一个实体.这将允许您应用第一个建议.你会有以下型号:
public partial class Location
{
public Location()
{
this.AttributeTypes = new List<LocationAttribute>();
}
public Location(int campusId, string code)
: this()
{
CampusId = campusId; Code = code;
}
public int Id { get; set; }
public int CampusId { get; set; }
public string Code { get; set; }
public virtual ICollection<LocationAttribute> AttributeTypes { get; set; }
}
public partial class LocationAttribute
{
[ForeignKey("LocationId")]
public Location Location { get; set; }
public int LocationId { get; set; }
public int AttributeTypeId { get; set; }
}
public partial class AttributeType
{
public int AttributeTypeId { get; set; }
public string AttributeTypeName { get; set; }
}
使用此方法会导致功能丢失,因为您无法在不进行中间查找的情况下从Location导航到AttributeType.如果你真的想这样做,你需要明确地控制实体状态. (当你想使用通用存储库时,这样做并不是那么简单,这就是我专注于这种方法的原因.)