我注意到EF的DbSet.Add()非常慢。谷歌搜索得到了一个可以提高180倍性能的答案:
https://stackoverflow.com/a/7052504/141172
但是,我并不完全理解如何按照答案中的建议实现IEquatable<T>。
根据MSDN,如果我实现了IEquatable<T>,我还应该重写Equals()和GetHashCode()。
和很多POCO一样,我的目标是可变的。在提交到数据库(SaveChanges())之前,新对象的Id为0。在保存对象之后,Id是实现IEquatable、Equals()和GetHashCode()的理想基础。
在哈希代码中包含任何可变属性是不明智的,因为根据MSDN
如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。
我是否应该将IEquatable<T>实现为按属性进行属性比较(例如,this.FirstName == other.FirstName),而不是覆盖等于()和GetHashCode()?
考虑到我的POCO是在EntityFramework上下文中使用的,是否应该特别注意Id字段?
发布于 2012-11-17 05:51:09
我偶然发现你的问题是为了寻找解决同样问题的方法。下面是我正在尝试的解决方案,看看它是否符合您的需要:
首先,我的所有POCOs都来自于这个抽象类:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
private readonly Guid _guid = Guid.NewGuid();
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof (T))
{
return false;
}
return Equals((T)obj);
}
public override int GetHashCode()
{
return _guid.GetHashCode();
}
}我创建了一个在GetHashCode()覆盖中使用的只读Guid字段。这将确保如果我将派生的POCO放入字典或其他使用哈希的东西中,如果我在过渡期间调用了.SaveChanges(),并且ID字段由基类更新--这是我不确定是否完全正确的部分,或者它是否比Base.GetHashCode()更好,那么我就不会孤立它。我抽象了相等(T其他)方法,以确保实现类必须以某种有意义的方式实现它,很可能是使用ID字段。我在这个基类中添加了相等(Object obj)覆盖,因为对于所有派生类,它可能也是相同的。
这将是抽象类的实现:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
}ID属性被设置为数据库中的主键,EF知道这一点。在新创建的对象上ID为0,然后在.SaveChanges()上设置为唯一的正整数。因此,在重写的等于(物种)方法中,空对象显然是不相等的,同样的引用显然是相等的,那么我们只需要检查ID == 0。如果是的话,我们会说两个ID都是0的相同类型的对象不相等。否则,如果它们的属性都是相同的,我们会说它们是相等的。
我认为这涵盖了所有相关的情况,但如果我不正确,请加入。希望这能有所帮助。
===编辑1
我认为我的GetHashCode()是不对的,我查看了关于这个主题的https://stackoverflow.com/a/371348/213169答案。上面的实现违反了返回等于() ==真的对象必须具有相同的哈希代码的约束。
这是我第二次尝试:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public abstract override bool Equals(object obj);
public abstract override int GetHashCode();
}以及执行情况:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return Equals(obj as Species);
}
public override int GetHashCode()
{
unchecked
{
return ((LegacyCode != null ? LegacyCode.GetHashCode() : 0) * 397) ^
(Name != null ? Name.GetHashCode() : 0);
}
}
public static bool operator ==(Species left, Species right)
{
return Equals(left, right);
}
public static bool operator !=(Species left, Species right)
{
return !Equals(left, right);
}
}因此,我去掉了基类中的Guid,并将GetHashCode移到实现中。除了ID之外,我使用了Resharper的GetHashCode实现,因为ID可能会更改(不要孤儿)。这将满足上述联系答案中对平等的限制。
发布于 2012-03-20 06:57:30
和许多POCO一样,我的对象是可变的
但是在作为主键的字段上,tehy不应该是可变的。每个定义,否则您是在一个痛苦数据库明智的世界无论如何以后。
只在主键的字段上生成HashCode。
Equals()必须返回真正的IFF参与对象具有相同的哈希代码
错误。
哈希码是双倍的。有可能有两个对象具有不同的值和smae哈希码。hsahsode是一个int (32位)。一个字符串可以有2gb长。您不能将每个可能的字符串都应用到单独的哈希代码中。
如果两个对象具有相同的哈希代码,它们可能是不同的。如果两个对象相同,则它们不能有不同的哈希码。
对于具有相同哈希码的对象,您从哪里得到了等于必须返回true的想法?
此外,无论是否PCO,映射到数据库并在关系中使用的对象必须具有稳定的主键(可用于运行哈希代码计算)。不具有此STIL l的对象应该具有主键(根据Server的要求),在这里使用序列/人工主键工作。同样,使用它来运行HashCode计算。
发布于 2012-04-27 14:14:59
第一件事:对不起我蹩脚的英语:)
正如TomTom所说,它们不应该仅仅因为它们还没有收到PK/Id而变.
在我们的EF:CF系统中,对于每个新的POCO,我们使用生成的负id (在基类ctor中分配,或者,如果您使用ProxyTracking,在ObjectMaterialized事件中)。它非常简单的想法:
public static class IdKeeper
{
private static int m_Current = int.MinValue;
private static Next()
{
return ++m_Current;
}
}MinValue和增量应该很重要,因为EF在对db进行更改之前将按PK对POCOs进行排序,并且当您使用"-1,-2,-3“时,POCOs会被翻转保存,在某些情况下(不是根据哪种类型)可能并不理想。
public abstract class IdBase
{
public virtual int Id { get; set; }
protected IdBase()
{
Id = IdKeeper.Next();
}
}如果POCO是从DB中实现的,那么他的Id将与实际的PK以及调用SaveChanges()时被覆盖。作为奖励,每一个“尚未保存”的POCO id都将是唯一的(总有一天会派上用场;)
然后,将两个POCO与IEquatable (为什么dbset工作这么慢?)进行比较很容易:
public class Person
: IdBase, IEquatable<Person>
{
public virtual string FirstName { get; set; }
public bool Equals(Person other)
{
return Id == other.Id;
}
}https://stackoverflow.com/questions/9782235
复制相似问题