2013-03-14 4 views
13

過去に誰かが解決策を見つけた疑いがあります。 AddOrUpdateメソッドを使用して、EF5移行のConfigurationクラスにデータベースをシードします。ここでEF5 Migrations Seed AddOrUpdate(ヌル選択可能条件付き)

は、ドメインモデルの簡単な例です:ここに続いて

public class Club 
{ 
    public virtual long Id { get; set; } 
    public virtual string Name { get; set; } 
} 

public class Court 
{ 
    public virtual long Id { get; set; } 
    public virtual string Name { get; set; } 

    public virtual long? ClubId { get; set; } 
    public virtual Club Club { get; set; } 
} 

は私の種子法からの抜粋です:今

Club cb = new Club { Name = "Test Club 1" }; 
context.Set<Club>().AddOrUpdate(m=>m.Name, cb); 
context.SaveChanges(); 

Court crt1 = new Court { ClubId = cb.Id, Name = "Court 1" }; 
Court crt2 = new Court { ClubId = cb.Id, Name = "Court 2" }; 
context.Set<Court>().AddOrUpdate(m => new { m.Name, m.ClubId }, crt1, crt2); 
context.SaveChanges(); 

、コード行番号7に達すると、それはスローエラー:

The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int64]' and 'System.Int64'.

私の調査によると、ClubIdはNullable型であるためです。

これを回避する手段はありますか?

ない大きな問題 - 私は非常にを持っていない私は完璧主義だし、他の人がこれを解決したかもしれないかを確認したいと思います...

おかげで、 ニックGoloborodko

+0

明確にするために、クラスにはもっと多くのプロパティがありますが、わかりやすくするために省略しました。 –

+0

私も同様の問題がありました。回避策として、私は別のパラメータに切り替えました。この場合、私はm.ClubIdをまったく使用しません。m.Name – jyrkim

答えて

10

私は回避策を適用したので、これを修正するためにAddOrUpdate実装にコードを変更する必要があると私は信じています。

簡単に言えば、AddOrUpdateを使用するのではなく、手動で同じ作業を行います。たとえば:

private void AddOrUpdateCourt(long id, string name, string someOtherPropertyValue) 
{ 
    var court = _context.Set<Court>().SingleOrDefault(c => c.Id = id && c.Name = name); 
    if(court == null) 
    { 
     _context.Set<Court>().Add(new Court 
     { 
      ClubId=id, 
      Name=name, 
      SomeOtherProperty = someOtherPropertyValue 
     }); 
    } 
    else 
    { 
     court.SomeOtherProperty = someOtherPropertyValue; 
    } 
} 
6

は、同じ問題を抱えていた最後にAddOrUpdate自分を実施しました。

まず、私たちは、エンティティの実際の主キーを取得する必要があります(多分あなたは...ここに追加の命名規則を追加する必要があります):

private static PropertyInfo[] PrimaryKeys<TEntity>() 
    where TEntity : class 
{ 
    return typeof(TEntity).GetProperties() 
          .Where(p => Attribute.IsDefined(p, typeof(KeyAttribute)) 
            || "Id".Equals(p.Name, StringComparison.Ordinal)) 
          .ToArray(); 
} 

をその後、私たちは「識別子の表現」を解析する必要があります、(オリジナルAddOrUpdateなど)既存のアイテムと一致するAddOrUpdateで使用される:

private static PropertyInfo[] Properties<TEntity>(
    Expression<Func<TEntity, object>> identifiers) 
    where TEntity : class 
{ 
    // e => e.SomeValue 
    var direct = identifiers.Body as MemberExpression; 
    if (direct != null) 
    { 
     return new[] { (PropertyInfo)direct.Member }; 
    } 

    // e => (object)e.SomeValue 
    var convert = identifiers.Body as UnaryExpression; 
    if (convert != null) 
    { 
     return new[] { (PropertyInfo)((MemberExpression)convert.Operand).Member }; 
    } 

    // e => new { e.SomeValue, e.OtherValue } 
    var multiple = identifiers.Body as NewExpression; 
    if (multiple != null) 
    { 
     return multiple.Arguments 
         .Cast<MemberExpression>() 
         .Select(a => (PropertyInfo)a.Member) 
         .ToArray(); 
    } 

    throw new NotSupportedException(); 
} 

したがって、.AddOrUpdate(m => m.Name, ...).AddOrUpdate(m => new { m.Name, m.ClubId }, ...)と同様に動作するはずです。

最後に、我々はすべてのエンティティのための動的表現は、私たちの現在のデータベースと照合を構築し、(追加または更新)に従って行動:

public static void AddOrUpdate<TEntity>(this DbContext context, 
    Expression<Func<TEntity, object>> identifiers, 
    params TEntity[] entities) 
    where TEntity : class 
{ 
    var primaryKeys = PrimaryKeys<TEntity>(); 
    var properties = Properties<TEntity>(identifiers); 

    for (var i = 0; i < entities.Length; i++) 
    { 
     // build where condition for "identifiers" 
     var parameter = Expression.Parameter(typeof(TEntity)); 
     var matches = properties.Select(p => Expression.Equal(
      Expression.Property(parameter, p), 
      Expression.Constant(p.GetValue(entities[i]), p.PropertyType))); 
     var match = Expression.Lambda<Func<TEntity, bool>>(
      matches.Aggregate((p, q) => Expression.AndAlso(p, q)), 
      parameter); 

     // match "identifiers" for current item 
     var current = context.Set<TEntity>().SingleOrDefault(match); 
     if (current != null) 
     { 
      // update primary keys 
      foreach (var k in primaryKeys) 
       k.SetValue(entities[i], k.GetValue(current)); 

      // update all the values 
      context.Entry(current).CurrentValues.SetValues(entities[i]); 

      // replace updated item 
      entities[i] = current; 
     } 
     else 
     { 
      // add new item 
      entities[i] = context.Set<TEntity>().Add(entities[i]); 
     } 
    } 
} 

希望疑問があるものの、これは、役立ちますビット古い。

+0

元のAddOrUpdateの代わりにこのメソッドを呼び出す方法はわかりません。 – StillLearnin

+0

'context.AddOrUpdate (e => e.Name、エンティティ)'または 'context.AddOrUpdate (e =>新しい{e.Some、e.Thing}、エンティティ)'です。元のAddOrUpdate(set、identifierExpression、entities)に似ていますが、DbSetの代わりにDbContextのためにビルドされている必要があります(両方使うことができます)。 –

+0

ああ!それがDbContext上に構築されているという事実は見逃していました。更新していただきありがとうございます。 – StillLearnin