2

我的问题是我有一个来自实体框架的“属性”属性。所以我检索了这个具有属性标签列表的对象,它们可以通过attribute.AttributeTags. 现在我有一个asp:TextBox用户可以编辑、删除和添加新标签(逗号分隔)的地方。(在页面加载时,我将属性标签添加到此TextBox

在页面上回发后,我将返回用户输入并将其拆分为字符串数组并将其存储在名为AttributeTags.

现在,我想添加未包含在attributes来自 EF 的原始列表中的新标签,并希望添加remove那些包含在attributes用户输入字符串数组中但未在其中找到的标签AttributeTags

我正在做这样的事情:

        BusinessObjects.Attribute attribute = db.Attributes.FirstOrDefault(a => a.attribute_id == AttributeID);
        string[] AttributeTags = txtAttributeTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var item in AttributeTags)
        {
            if (!attribute.AttributeTags.Any(t => t.value == item))
            {
                AttributeTag tag = new AttributeTag { value = item, timestamp = DateTime.Now };
                attribute.AttributeTags.Add(tag);
            }
            else
            {
                AttributeTag tag = attribute.AttributeTags.FirstOrDefault(t => t.value == item);
            }
        }

但是我有点卡在这里,因为我对 LINQ 和 EF 还很陌生。

4

6 回答 6

2

对于这种情况,我有两种解决方案。


第一个解决方案

我们可以创建一个ExcepWith方法,允许我们删除 aICollection<T>中已经存在的所有项目 give a IEnumerable<T>。这种方法的代码如下:

public static int ExceptWith<TItem>
(
    this ICollection<TItem> collection,
    IEnumerable<TItem> other
)
{
    if (ReferenceEquals(collection, null))
    {
        throw new ArgumentNullException("collection");
    }
    else if (ReferenceEquals(other, null))
    {
        throw new ArgumentNullException("other");
    }
    else
    {
        int count = 0;
        foreach (var item in other)
        {
            while (collection.Remove(item))
            {
                count++;
            }
        }
        return count;
    }
}

现在您有了string[]用户的输入,该数组是 anIEnumerable<string>但不是 an ICollection<string>...,很容易解决如下:

而不是这个:

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );

你来做这件事:

var AttributeTags =
    new List<string>
    (
        txtAttributeTags.Text.Split
        (
            new string[] { "," },
            StringSplitOptions.RemoveEmptyEntries
        )
    );

甚至这样:

var AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    ).ToList();

现在你可以这样做:

AttriuteTags.ExceptWith(existingTags);

由于attribute.AttributeTag的类型不是IEnumerable<string>你使用Select:

AttriuteTags.ExceptWith(attribute.AttributeTag.Select(item => item.value));

这样就只剩下列表中的新标签了。


注意:这个方法依赖于 Remove 的实现,如果你需要做一个特殊的比较,那么你不走运这个方法。


第二种解决方案

还有另一种方法。您可以使用类中的“除外Enumerable

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );
var newTags = AttributeTags.Except(existingTags);

由于attribute.AttributeTag的类型不是IEnumerable<string>你使用Select:

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );
var newTags = AttributeTags.Except
(
    attribute.AttributeTag.Select(item => item.value)
);

这放入了新标签,好吧,新标签。


注意:如果你需要做一个特殊的比较,那么你应该使用方法的其他重载

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );
var newTags = AttributeTags.Except(attribute.AttributeTag, equalityComparer);

遗憾的是,equalComparer 是实现 IEqualityComparer 的类的对象,这意味着您不能在其中使用 lambda。为此,您可以添加此类:

public class CustomEqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _comparison;
    private Func<T, int> _getHashCode;

    public CustomEqualityComparer
    (
        Func<T, T, bool> comparison,
        Func<T, int> getHashCode
    )
    {
        if (ReferenceEquals(comparison, null))
        {
            throw new ArgumentNullException("comparison");
        }
        else if (ReferenceEquals(getHashCode, null))
        {
            throw new ArgumentNullException("getHashCode");
        }
        else
        {
           _comparison = comparison;
           _getHashCode = getHashCode;
        }
    }

    public bool Equals(T x, T y)
    {
        return _comparison.Invoke(x, y);
    }

    public int GetHashCode(T obj)
    {
        return _getHashCode.Invoke(obj);
    }
}

现在像这样调用(例如):

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );
var newTags = AttributeTags.Except
(
    existingTags,
    new CustomEqualityComparer<string>
    (
        (a, b) => 1, //your custom comparison here
        str => str.GetHashCode()
    )
);

由于attribute.AttributeTag的类型不是IEnumerable<string>你使用Select:

string[] AttributeTags =
    txtAttributeTags.Text.Split
    (
        new string[] { "," },
        StringSplitOptions.RemoveEmptyEntries
    );
var newTags = AttributeTags.Except
(
    attribute.AttributeTag.Select(item => item.value),
    new CustomEqualityComparer<string>
    (
        (a, b) => 1, //your custom comparison here
        str => str.GetHashCode()
    )
);

添加新标签

现在您有了新标签,假设在 中newTags,您可以对其进行迭代以添加新标签:

var now = DateTime.Now;
foreach (var item in newTags)
{
    AttributeTag tag = new AttributeTag { value = item, timestamp = now };
    attribute.AttributeTags.Add(tag);
}

比较解决方案

这些方法有什么区别?

  • 第一个需要更少的内存
  • 第一个需要定义一个新方法。
  • 第一个不允许自定义IEqualityComparer<T>
  • 第二个允许延迟执行。
  • 第二个使用(不需要)辅助类。
于 2012-10-12T05:33:11.267 回答
2

如何做你想做的事的一个简化例子。

var fromDB = new List<string>() { "a", "b", "c", "d", "e" };
var userInput = new List<string>() { "c", "d", "e", "f", "g" };
var result = fromDB.Join(userInput, x => x, y => y, (x, y) => x).Union(userInput);

现在您所要做的就是用结果替换数据库内容。

于 2012-10-12T06:16:10.287 回答
1

使用 Iesi.Collections 可以非常优雅地解决这个问题。它有多种实现方式,这里有一个:Set Collections

ListSet set1 = new ListSet(new [] {"1","2","8"});
ListSet set2 = new ListSet(new [] {"8","16","32"});
var union = set1 | set2;        // "1","2","8","16","32"
var intersect = set1 & set2;    // "8"
var diff = set1 ^ set2;         // "1","2","16","32"
var minus = set1 - set2;        // "1","2"
于 2012-10-16T06:39:23.867 回答
1

这是我测试的代码。在实体框架中有很多保存方法。

注意:请确保在迭代集合时不要修改/删除项目。

在此处输入图像描述

<asp:TextBox ID="txtAttributeTags" runat="server" />
<asp:Button runat="server" ID="SubmitButton" OnClick="SubmitButton_Click" 
  Text="Submit" />

public const int AttributeID = 1;

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
    using (var db = new AttributeEntities())
    {
      var tags = db.AttributeTags
        .Where(a => a.attribute_id == AttributeID)
        .Select(a => a.value);

      txtAttributeTags.Text = string.Join(",", tags);
    }
  }
}

protected void SubmitButton_Click(object sender, EventArgs e)
{
  using (var db = new AttributeEntities())
  {
    string[] newTags = txtAttributeTags.Text.Split(new[] {","}, 
      StringSplitOptions.RemoveEmptyEntries);

    var oldTags = db.AttributeTags.Where(t => t.attribute_id == AttributeID);

    foreach (var tag in oldTags.Where(o => !newTags.Contains(o.value)))
        db.AttributeTags.DeleteObject(tag);

    foreach (var tag in newTags.Where(n => !oldTags.Any(o => o.value == n)))
      db.AttributeTags.AddObject(new AttributeTag
      {
          attribute_id = AttributeID, value = tag, timestamp = DateTime.Now
      });

    db.SaveChanges();
    }
  }
}
于 2012-10-16T18:57:23.040 回答
0

我们对 db 对象的 Attributes 属性使用 Remove 方法,然后保存更改

 db.Attributes.Remove( object );

然后将更改保存到 db 对象。

如果我正确假设您的 db 对象是 EF 中的连接对象,这应该可以工作。

于 2012-10-09T17:50:14.843 回答
0

我不能做一个完整的测试,但应该做一些类似的事情:

BusinessObjects.Attribute attribute = db.Attributes.FirstOrDefault(a => a.attribute_id == AttributeID);
string[] AttributeTags = txtAttributeTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

foreach (var item in from a in AttributeTags
                     where attribute.AttributeTags.Any(t => t.value == a)
                     select new AttributeTag 
                     { 
                         value = item, 
                         timestamp = DateTime.Now 
                     })
    attribute.AttributeTags.Add(item);

foreach (var item in from a in attribute.AttributeTags
                     where AttributeTags.Any(t => t == a.value)
                     select a)
    attribute.AttributeTags.Remove(item);

db.SaveChanges();
于 2012-10-09T19:28:43.037 回答