我的问题是我有一个来自实体框架的'属性'属性。
所以我检索这个有一个属性标签列表的对象,可以通过attribute.AttributeTags
访问它们。现在我有asp:TextBox
用户可以编辑,删除和添加新标签(以逗号分隔)。 (在页面加载时,我将属性标记添加到此TextBox
)
在页面上回发后,我返回用户输入并将其拆分为字符串数组并将其存储在名为AttributeTags
的变量中。
现在,我想添加来自EF的原始attributes
列表中未包含的新标记,并希望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很新。
答案 0 :(得分:2)
我有两种解决方案。
我们可以创建一个ExcepWith
方法,允许我们移除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[]
的用户输入,该数组是IEnumerable<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的实现,如果你需要做一个特殊的比较,那么你对这个方法运气不好。
还有另一种方式。您可以使用Enumerable
类中的Except。
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)
);
这就是新标签,新标签。
注意:如果您需要进行特殊比较,那么您应该使用the other overload of the method:
string[] AttributeTags =
txtAttributeTags.Text.Split
(
new string[] { "," },
StringSplitOptions.RemoveEmptyEntries
);
var newTags = AttributeTags.Except(attribute.AttributeTag, equalityComparer);
遗憾的是,equalityComparer是实现IEqualityComparer的类的对象,这意味着你不能在那里使用lambdas。为此你可以添加这个类:
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>
答案 1 :(得分: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);
现在您所要做的就是用结果替换数据库内容。
答案 2 :(得分: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"
答案 3 :(得分: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();
}
}
}
答案 4 :(得分:0)
我们在db对象的Attributes属性上使用Remove方法然后保存更改
db.Attributes.Remove( object );
然后将更改保存到db对象。
如果我正确地假设您的db对象是EF中的连接对象,那么这应该有用。
答案 5 :(得分: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();