C# EF将重复记录添加到查找/引用表中
我有三张桌子, 1.AttributeType(列:AttributeId(PK)、AttributeName、…) 2.位置(列:locationId(PK)、LocationName等) 3.LocationAttributeType(列:locationId(FK)、AttributeId(FK)) 每当我试图从GUI插入新的位置记录及其属性类型时,它都应该为表-location和LocationAttributeType创建新记录。但EF也试图在表-AttributeType中添加新记录,该表仅用作参考表,不应在其中添加新的/重复的记录。我怎样才能防止呢 这是我的密码 GUI发送的模型是C# EF将重复记录添加到查找/引用表中,c#,sql-server,entity-framework,ef-code-first,C#,Sql Server,Entity Framework,Ef Code First,我有三张桌子, 1.AttributeType(列:AttributeId(PK)、AttributeName、…) 2.位置(列:locationId(PK)、LocationName等) 3.LocationAttributeType(列:locationId(FK)、AttributeId(FK)) 每当我试图从GUI插入新的位置记录及其属性类型时,它都应该为表-location和LocationAttributeType创建新记录。但EF也试图在表-AttributeType中添加新记录
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; }
}
公共类LocationDataModel
{
[数据成员]
公共int Id{get;set;}
[数据成员]
公共字符串代码{get;set;}
[数据成员]
public List AssignedAttributes=新列表();
}
公共类attributeTypedAtModel
{
受保护的AttributeTypedAtModel(){}
public attributeTypedAtModel(int-id){this.id=id;}
public attributeTypedAtModel(int-id,字符串名)
:此(id)
{
this.Name=Name;
}
[数据成员]
公共int Id{get;set;}
[数据成员]
公共字符串名称{get;set;}
[数据成员]
公共虚拟ICollection位置{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; }
公共部分类位置
{
公共场所()
{
this.attributeType=新列表();
}
公共位置(int-campusId,字符串代码)
:此()
{
CampusId=CampusId;Code=Code;
}
公共int Id{get;set;}
公共int CampusId{get;set;}
公共字符串代码{get;set;}
公共虚拟ICollection属性类型{get;set;}
}
公共部分类AttributeType
{
公共属性类型()
{
this.Locations=新列表();
}
public int AttributeTypeId{get;set;}
公共字符串AttributeTypeName{get;set;}
公共虚拟ICollection位置{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();
}
私有IEnumerable加法
(IEnumerable models,Func primaryKey,
IGenericRepository存储库)
{
var results=新列表();
foreach(模型中的var模型)
{
var merged=\u mapper.Map(模型);
var entity=repository.Upsert(合并);
结果:增加(实体);
}
Save();
返回结果;
}
我使用以下通用存储库来执行与实体相关的操作
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();
}
public tenty Upsert(tenty实体)
{
if(等于(PrimaryKey.Invoke(entity),默认值(TId)))
{
//新实体
返回Context.Set().Add(实体);
}
其他的
{
//现有实体
Context.Entry(entity.State=EntityState.Modified;
返回实体;
}
}
公共作废保存()
{
SaveChanges();
}
我在这里做错了什么?在您的
Upsert
的else
语句中,您应该添加
context.TEntity.Attach(entity);
DbSet.Add()。您需要向EF指出“引用”实体实际上已经存在。有两种简单的方法可以做到这一点:
- 不要将导航属性设置为对象。相反,只需将相应的外键属性设置为正确的值
- 您需要确保不会将同一实体的多个实例加载到对象上下文中。创建上下文后,将
AttributeType
实体的完整列表加载到上下文中,并创建一个字典来存储它们。如果要将属性添加到位置
中,请从字典中检索相应的属性。调用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();
}
使用(MyContext c=new MyContext())
{
c、 Add(新的AttributeType{AttributeTypeName=“Fish”,AttributeTypeId=1});
c、 Add(新的AttributeType{AttributeTypeName=“Face”,AttributeTypeId=2});
c、 保存更改();
}
使用(MyContext c=new MyContext())
{
字典=新字典();
foreach(c.attributeType中的变量t)
{
字典[t.AttributeTypeId]=t;
}
位置l1=新位置(1,“位置1”){attributeType={dictionary[1],dictionary[2]};
位置l2=新位置(2,“位置2”){attributeType={dictionary[1]};
//由于LocationType已附加到上下文,因此不会重新添加它。
c、 位置。添加(l1);
c、 位置。添加(l2);
c、 保存更改();
}
在这种特定情况下,您使用的是一种多对多关系,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; }
}
公共部分类位置
{
公共场所()
{
this.attributeType=新列表();
}
公共位置(int-campusId,字符串代码)
:此()
{
CampusId=CampusId;Code=Code;
}
公共int Id{get;set;}
公共int CampusId{get;set;}
公共字符串代码{get;set;}
公共虚拟ICollection属性IP
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; }
}
private IEnumerable<int> AddLocationEntities(IEnumerable<LocationDataModel> locations)
{
var results = new List<int>();
foreach (LocationDataModel l in locations)
{
var entity = _mapper.Map<LocationDataModel, Location>(l);//you can map manually also
var AttributeCode = l.AssignedAttributes.FirstOrDefault().AttributeTypeId;
using (MyContext c = new MyContext())
{
var attr = c.AttributeTypes.Where(a => a.Id == AttributeTypeId ).ToList();
entity.AttributeTypes = attr;
c.Locations.Add(entity);
c.SaveChanges();
var locid = entity.Id;
results.Add(locid);
}
}
return results;
}