4

我有 3 个表,1. AttributeTypes(列:AttributeId(PK),AttributeName,..) 2. Location(列:locationId(PK),LocationName,...) 3. LocationAttributeType(列:locationId(FK),AttributeId (FK))

每当我尝试从 GUI 插入新的位置记录及其属性类型时,它应该为 Table- LocationLocationAttributeType创建新记录。但 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();
    }

我在这里做错了什么?

4

3 回答 3

1

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; }
}

使用这种方法,您确实会失去功能,因为您无法在不进行中间查找的情况下从 a 导航Location到 an 。AttributeType如果你真的想这样做,你需要明确地控制实体状态。(当您想使用通用存储库时,这样做并不那么简单,这就是我专注于这种方法的原因。)

于 2013-11-11T21:53:22.763 回答
1

谢谢大家的建议。我必须在这里摆脱我的通用存储库以保存我的上下文更改并手动执行如下操作,

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;
    }
于 2013-11-16T23:22:52.180 回答
0

在您的else陈述中,您Upsert应该添加

context.TEntity.Attach(entity);
于 2013-11-11T21:49:32.630 回答