Correct way to remove a many-to-many relationship via linq to sql?

前端 未结 3 1886
广开言路
广开言路 2021-01-20 19:48

Let\'s say we have two tables with a many-to-many relationship:

public class Left{ /**/ }

public class Right{ /**/ }

public class LeftRight{ /**/ }
         


        
3条回答
  •  逝去的感伤
    2021-01-20 20:34

    Take two, using expressions:

    public static class EntitySetExtensions
    {
      public static void UpdateReferences(
          this EntitySet refs,
          Expression> fkexpr,
          IEnumerable values)
        where FK : class
        where FKV : class
      {
        Func fkvalue = fkexpr.Compile();
        var fkmaker = MakeMaker(fkexpr);
        var fkdelete = MakeDeleter(fkexpr);
    
        var fks = refs.Select(fkvalue).ToList();
        var added = values.Except(fks);
        var removed = fks.Except(values);
    
        foreach (var add in added)
        {
          refs.Add(fkmaker(add));
        }
    
        foreach (var r in removed)
        {
          var res = refs.Single(x => fkvalue(x) == r);
          refs.Remove(res);
          fkdelete(res);
        }
      }
    
      static Func MakeMaker(Expression> fkexpr)
      {
        var me = fkexpr.Body as MemberExpression;
    
        var par = Expression.Parameter(typeof(FKV), "fkv");
        var maker = Expression.Lambda(
            Expression.MemberInit(Expression.New(typeof(FK)), 
              Expression.Bind(me.Member, par)), par);
    
        var cmaker = maker.Compile() as Func;
        return cmaker;
      }
    
      static Action MakeDeleter(Expression> fkexpr)
      {
        var me = fkexpr.Body as MemberExpression;
        var pi = me.Member as PropertyInfo;
    
        var par = Expression.Parameter(typeof(FK), "fk");
        var maker = Expression.Lambda(
            Expression.Call(par, pi.GetSetMethod(), 
              Expression.Convert(Expression.Constant(null), typeof(FKV))), par);
    
        var cmaker = maker.Compile() as Action;
        return cmaker;
      }
    }
    

    Now the usage is uber simple! :)

    Left entity = ...;
    IEnumerable rights = ...;
    
    entity.LeftRights.UpdateReferences(x => x.Right, rights);
    

    The first expression is now used to establish the 'relationship'. From there I can infer the 2 previously required delegates. Now no more :)

    Important:

    To get this to work properly in Linq2Sql, you need to mark the associations from intermediary table with 'DeleteOnNull="true"' in the dbml file. This will break the designer, but still works correctly with SqlMetal.

    To unbreak the designer, you need to remove those additional attributes.

提交回复
热议问题