2

私の問題は、エンティティ フレームワークからの「属性」としての属性があることです。したがって、属性タグのリストを持つこのオブジェクトを取得します。それらは 経由でアクセスできますattribute.AttributeTags。これasp:TextBoxで、ユーザーが新しいタグ (カンマ区切り) を編集、削除、および追加できる場所ができました。(ページの読み込み時に、これに属性タグを追加していますTextBox)

ページでポストバックした後、ユーザー入力を返し、それを文字列の配列に分割し、変数AttributeTags.

attributesここで、 EF からの元のリストに含まれていない新しいタグを追加し、ユーザー入力文字列配列にremove含まれているが見つからないタグ​​を追加したいと考えています。attributesAttributeTags

私はこのようなことをしています:

        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

この状況には2つの解決策があります。


最初の解決策

既に与えられているExcepWitha 内のすべてのアイテムを削除できるメソッドを作成できます。このようなメソッドのコードは次のとおりです。ICollection<T>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>ではなく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 の実装に依存します。特別な比較を行う必要がある場合は、このメソッドではうまくいきません。


2 番目のソリューション

別の方法があります。クラスからの例外を使用できます。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)
);

これにより、newTags、つまり新しいタグが挿入されます。


: 特別な比較を行う必要がある場合は、メソッドの他のオーバーロードを使用する必要があります。

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

悲しいことに、equalityComparer は IEqualityComparer を実装するクラスのオブジェクトです。つまり、そこでラムダを使用することはできません。そのために、このクラスを追加できます:

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

ソリューションの比較

これらの方法の違いは何ですか?

  • 1 つ目は必要なメモリが少ない
  • 1 つ目は、新しいメソッドを定義する必要があります。
  • 最初はカスタムを許可しませんIEqualityComparer<T>
  • 2 つ目は遅延実行を可能にします。
  • 2 つ目は (必要ではありませんが) ヘルパー クラスを使用します。
于 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

これが私がテストしたコードです。エンティティ フレームワークに保存する方法はたくさんあります。

注: コレクションの反復中にアイテムを変更/削除しないようにしてください。

ここに画像の説明を入力

<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 に答える
1

この問題は、 Iesi.Collectionsを使用して非常にエレガントに解決できます。これにはいくつかの実装があり、ここにその 1 つを示します。

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 に答える
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 に答える