1

我在 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() 方法似乎没有删除上下文中的任何链接。

我该如何去删除现有的链接,还是我处理这一切都错了?

4

3 回答 3

2

我在 Silverlight 项目中遇到了同样的问题。我采用了对我有用的解决方案并将其应用于您的用户/安全组模型。

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

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

从你上面的代码。

于 2012-10-04T18:33:02.573 回答
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.

于 2012-07-05T17:24:32.740 回答
1

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

  1. 这是通用的
  2. AddSecurityGroup 也应该附加链接,如果它以前被删除
  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;
        }
    }
    

用法:

var ctx = new YourContext();
ctx.AddOrAttachLink(addTo, () => addTo.Collection, toAdd);
ctx.DeleteOrDetachLink(removeFrom, () => removeFrom.Collection, toRemove);
于 2014-11-06T16:14:41.960 回答