使用WCF数据服务从集合中删除项目

时间:2012-07-05 16:02:49

标签: c# wcf-data-services odata

我在Windows窗体应用程序中有一个选中的列表框,允许用户将一个或多个安全组分配给选定的用户。

使用WCF数据服务,我可以毫无问题地填充框。但是,当用户更改框中的选择并尝试保存这些更改时,我遇到了问题。

这是代码,用注释来解释我的逻辑。

private void ProcessSecurityGroupSelection_Original()
{
  //Get a reference to the selected user, including the associated SecurityGroups.
  var user = _ctx.Users
    .AddQueryOption("$filter", "UserID eq " + ((DataService.User)lstUsers.SelectedItem).UserID)
    .AddQueryOption("$expand", "SecurityGroups")
    .First();

  //Remove all the SecurityGroups so we can replace them.
  user.SecurityGroups.Clear();

  foreach (var selectedGroup in lstSecurityGroups.CheckedItems)
  {
    //Loop through the selected SecurityGroups, linking and adding each SecurityGroup to the User object.
    var securityGroup = (from sg in _ctx.SecurityGroups
                 where sg.SecurityGroupID == ((DataService.SecurityGroup)selectedGroup).SecurityGroupID
                 select sg).First();

    _ctx.AddLink(user, "SecurityGroups", securityGroup);

    user.SecurityGroups.Add(securityGroup);
  }

  _ctx.UpdateObject(user);
  _ctx.SaveChanges();
}

当代码点击先前已被选中的SecurityGroup的AddLink方法时,我收到一条错误,指出“上下文已经在跟踪关系”。 Clear()方法似乎不会删除上下文中的任何链接。

如何删除现有链接,或者我是否接近这一切?

3 个答案:

答案 0 :(得分:2)

我遇到了与silverlight项目相同的问题。我已经采用了适合我的解决方案并将其应用于您的User / SecurityGroup模型。

将以下内容添加到您的用户类:

public User()
    {
    this.SecurityGroups.CollectionChanged += (sender, e) =>
        {
        if (e.Action == Add)
            {
            foreach (SecurityGroup AddedGroup in e.NewItems)
            AddSecurityGroup(AddedGroup);
            }
        if (e.Action == Remove)
            {
            foreach (SecurityGroup RemovedGroup in e.OldItems)
            RemoveSecurityGroup(RemovedGroup);
            }

        };

    ..... rest of constructor
    }



public void AddSecurityGroup(SecurityGroup secGroup)
    {
    LinkDescriptor descriptr = _ctx.GetLinkDescriptor(this, "SecurityGroups", secGroup);

    if (descriptr == null)
        _ctx.AddLink(this, "SecurityGroups", secGroup);

    else if (descriptr.State == EntityStates.Deleted)
        _ctx.DetachLink(this, "SecurityGroups", secGroup);

    }


public void RemoveSecurityGroup (SecurityGroup secGroup)
    {
    LinkDescriptor descriptr = _ctx.GetLinkDescriptor(this, "SecurityGroups", secGroup);

    if (descriptr == null)
        {
        _ctx.AttachLink(this, "SecurityGroups", secGroup);
        _ctx.DeleteLink(this, "SecurityGroups", secGroup);
        }

    else if (descriptr.State == EntityStates.Added)
        _ctx.DetachLink(this, "SecurityGroups", secGroup);

    else
        _ctx.DeleteLink(this, "SecurityGroups", secGroup);

    }

现在删除该行:

_ctx.AddLink(user, "SecurityGroups", securityGroup);  

来自上面的代码。

答案 1 :(得分:1)

删除链接的一种方法是添加如下事件:

private void lstSecurityGroups_ItemCheck(object sender, ItemCheckEventArgs e)
{
    if (e.NewValue == CheckState.Unchecked)
    {
        _ctx.DetachLink(user, "SecurityGroups", securityGroup);
    }
    else if (e.NewValue == CheckState.Checked)
    {
        _ctx.AddLink(user, "SecurityGroups", securityGroup);
    }
}

请注意,DeleteLink也会标记要删除的实体,如果多次调用会导致错误。如果您只想删除链接,请使用DetachLink

答案 2 :(得分:1)

根据Aiston的回答,我准备了一套扩展来帮助处理添加然后删除和删除然后添加的链接。

  1. 这是通用的
  2. 如果之前已删除,则附加链接
  3. 我更喜欢以一些小的性能成本来避免魔术字符串

    public static class DataServiceContextExtensions
    {
        public static void AddOrAttachLink<TSource>(this DataServiceContext context, object source, Expression<Func<TSource>> sourceProperty, object target)
        {
            AddOrAttachLink(context, source, sourceProperty.GetExpressionMemberInfo().Name, target);
        }
    
        public static void AddOrAttachLink<TSource, TTarget>(this DataServiceContext context, TSource source, Expression<Func<TSource, ICollection<TTarget>>> sourceProperty, TTarget target)
        {
            AddOrAttachLink(context, source, sourceProperty.GetExpressionMemberInfo().Name, target);
        }
    
        public static void AddOrAttachLink(this DataServiceContext context, object source, string propertyName, object target)
        {
            var descriptor = context.GetLinkDescriptor(source, propertyName, target);
            if(descriptor == null)
            {
                context.AddLink(source, propertyName, target);
            }
            else if(descriptor.State == EntityStates.Deleted)
            {
                context.DetachLink(source, propertyName, target);
                context.AttachLink(source, propertyName, target);
            }
        }
    
        public static void DeleteOrDetachLink<TSource>(this DataServiceContext context, object source, Expression<Func<TSource>> sourceProperty, object target)
        {
            DeleteOrDetachLink(context, source, sourceProperty.GetExpressionMemberInfo().Name, target);
        }
    
        public static void DeleteOrDetachLink<TSource, TTarget>(this DataServiceContext context, TSource source, Expression<Func<TSource, ICollection<TTarget>>> sourceProperty, TTarget target)
        {
            DeleteOrDetachLink(context, source, sourceProperty.GetExpressionMemberInfo().Name, target);
        }
    
        public static void DeleteOrDetachLink(this DataServiceContext context, object source, string propertyName, object target)
        {
            var descriptor = context.GetLinkDescriptor(source, propertyName, target);
            if(descriptor == null)
            {
                context.AttachLink(source, propertyName, target);
                context.DeleteLink(source, propertyName, target);
            }
            else if(descriptor.State == EntityStates.Added)
            {
                context.DetachLink(source, propertyName, target);
            }
            else
            {
                context.DeleteLink(source, propertyName, target);
            }
        }
    
        public static MemberInfo GetExpressionMemberInfo(this Expression expression)
        {
            var lambda = (LambdaExpression)expression;
    
            MemberExpression memberExpression;
            if(lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)lambda.Body;
                memberExpression = (MemberExpression)unaryExpression.Operand;
            }
            else
            {
                memberExpression = (MemberExpression)lambda.Body;
            }
    
            return memberExpression.Member;
        }
    }
    
  4. 用法:

    var ctx = new YourContext();
    ctx.AddOrAttachLink(addTo, () => addTo.Collection, toAdd);
    ctx.DeleteOrDetachLink(removeFrom, () => removeFrom.Collection, toRemove);