4

1. AttributeTypes (列: AttributeId (PK), AttributeName, ..) 2. Location (列: 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();
    }

私はここで何をしているのですか?

4

3 に答える 3

1

このDbSet<T>.Add()メソッドは、追加されたオブジェクト グラフ全体をアタッチします。「参照」エンティティが実際に既に存在することを EF に示す必要があります。これを行うには、次の 2 つの簡単な方法があります。

  • ナビゲーション プロパティをオブジェクトに設定しないでください。代わりに、対応する外部キー プロパティを正しい値に設定するだけです。

  • 同じエンティティの複数のインスタンスをオブジェクト コンテキストに読み込まないようにする必要があります。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 プロパティを公開していないことを意味し、上記の最初の提案は機能しません

したがって、まだ機能するはずの 2 番目の提案を使用するか、中間テーブルの自動処理をやめて代わりにエンティティを作成する必要があります。これにより、最初の提案を適用できます。次のモデルがあります。

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

このアプローチでは、中間ルックアップを行わないとからにナビゲートできないため、機能が失われます。本当にそうしたい場合は、代わりにエンティティの状態を明示的に制御する必要があります。(一般的なリポジトリを使用する場合、これを行うのはそれほど簡単ではありません。そのため、代わりにこのアプローチに焦点を当てています。)LocationAttributeType

于 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 に答える