.net EF 4.1代码查找/更新中的第一个错误。有解决办法吗?应该报告吗?

.net EF 4.1代码查找/更新中的第一个错误。有解决办法吗?应该报告吗?,.net,entity-framework,code-first,.net,Entity Framework,Code First,我首先在EF4.1代码中发现了一个相当严重的错误。假设我们有这段代码从上下文中检索实体,然后用新值更新它: public T Update<T>(T toUpdate) where T : class { System.Data.Objects.ObjectContext objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)_context).Obj

我首先在EF4.1代码中发现了一个相当严重的错误。假设我们有这段代码从上下文中检索实体,然后用新值更新它:

public T Update<T>(T toUpdate) where T : class
        {
            System.Data.Objects.ObjectContext objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)_context).ObjectContext;
            System.Data.Objects.ObjectSet<T> set = objectContext.CreateObjectSet<T>();
            IEnumerable<string> keyNames = set.EntitySet.ElementType
                                                        .KeyMembers
                                                        .Select(k => k.Name);
            var type = typeof(T);
            var values = keyNames.Select(c => type.GetProperty(c).GetValue(toUpdate, null)).ToArray();
            var current = _context.Set<T>().Find(values);
            if (current != null)
            {
                _context.Entry(current).CurrentValues.SetValues(toUpdate);
            }
            return current;
        }
public T Update(T toUpdate),其中T:class
{
System.Data.Objects.ObjectContext ObjectContext=((System.Data.Entity.Infrastructure.IOObjectContextAdapter)_context).ObjectContext;
System.Data.Objects.ObjectSet=objectContext.CreateObjectSet();
IEnumerable keyNames=set.EntitySet.ElementType
.主要成员
.选择(k=>k.Name);
var类型=类型(T);
var values=keyNames.Select(c=>type.GetProperty(c).GetValue(toUpdate,null)).ToArray();
var current=_context.Set().Find(值);
如果(当前!=null)
{
_context.Entry(当前).CurrentValues.SetValues(toUpdate);
}
回流;
}
现在假设我的实体有一个键属性,它是一个字符串

工作场景:存储实体具有键“ABCDE”,而我的toUpdate实体具有相同的键“ABCDE”:一切正常

错误场景:存储实体的键为“ABCDE”,而我的toUpdate实体的键为“ABCDE”(注意最后一个字母后面的空格)

这两把钥匙确实不同。但是find方法“自动”修剪了我的密钥并找到了存储的实体。如果它没有破坏SetValues方法,这将是很好的:因为存储的键和新键是不同的,我(正确地)得到以下结果:

属性“Id”是对象密钥信息的一部分,不能 可以修改

因为,不同的是,它试图更新它,而且由于它是一个关键属性,所以无法更新,所以整个过程失败并抛出

我认为“Find”方法不应该自动修剪键值(或者它在内部所做的任何事情,以使两个不同的字符串看起来相同)。在第二个场景中,“Find”方法应该返回null

现在有两件事:我如何暂时解决这个问题,我在哪里可以报告这个bug,因为我找不到一个正式的地方来报告这个bug

谢谢

编辑:在此处报告错误:

但是find方法“自动”修剪我的密钥并查找存储的 反正是实体

我不相信修剪会发生
Find
在内部使用
SingleOrDefault
查询,换句话说:当您调用

set.Find("ABCDE "); // including the trailing blank
…它使用此LINQ查询:

set.SingleOrDefault(key => key == "ABCDE "); // including the trailing blank
问题出在数据库中,结果取决于数据库中
字符串
键字段的排序顺序、语言、大写/小写字母设置、重音等(
nvarchar(10)

例如,如果在SQL Server中使用标准的
Latin1\u General
排序顺序,则键“ABCDE”“ABCDE”(尾随空白)是相同的,则不能创建将这些值作为主键的两行。甚至“ABCDE”“ABCDE”都是相同的(如果您没有在SQL Server中设置为区分大写字母和小写字母)

同时,这意味着对
string
列的查询也将返回所有匹配的行-与数据库中该列的排序顺序匹配。查询带有尾随空格的“ABCDE”将只返回带有“ABCDE”且不带尾随空格的记录

到目前为止,这是涉及字符串的所有LINQ到实体查询的“正常”行为

现在,正如您所发现的那样,ObjectContext似乎不知道数据库中配置的排序顺序,而是使用普通的.NET字符串比较,其中尾随空白的字符串和尾随空白的字符串是不同的

我不知道是否可以告诉上下文使用与数据库相同的字符串比较。我怀疑这是可能的,因为.NET世界和关系数据库世界太不一样了。某些排序顺序可能是特殊的,仅在数据库中可用,而在.NET中根本不可用,反之亦然。此外,除SQL Server之外,还有其他数据库必须由实体框架支持,这些数据库可能有自己的排序顺序系统

对于您的特定情况(可能总是在您有
string
keys时),解决问题的可能方法是将实体的key属性设置为更新为从数据库返回的对象的key:

toUpdate.Id = current.Id;
_context.Entry(current).CurrentValues.SetValues(toUpdate);
或者更一般地在代码的上下文中:

//...
var current = _context.Set<T>().Find(values);
if (current != null)
{
    foreach (var keyName in keyNames)
    {
        var currentValue = type.GetProperty(keyName).GetValue(current, null);
        type.GetProperty(keyName).SetValue(toUpdate, currentValue, null);
    }
    _context.Entry(current).CurrentValues.SetValues(toUpdate);
}
/。。。
var current=_context.Set().Find(值);
如果(当前!=null)
{
foreach(键名中的var键名)
{
var currentValue=type.GetProperty(keyName).GetValue(current,null);
type.GetProperty(keyName).SetValue(toUpdate,currentValue,null);
}
_context.Entry(当前).CurrentValues.SetValues(toUpdate);
}
toUpdate
不能附加到上下文以使其正常工作


这是虫子吗?我不知道。至少这是.NET和关系数据库世界不匹配的结果,也是一个从一开始就避免
string
键列/属性的好理由。

对于可以使用的报告错误,我会报告它。似乎是一个很好的错误报告。伟大的发现!实体框架在那个网站上什么地方都看不到。只是在这里遇到了同样的事情——就我而言,它肯定是一个bug。这是我发现的问题的极好阐述。我明天会试试你建议的解决办法。我仍然认为EF应该更好地处理这个问题。当然,这种方法被认为是用于分离的“toUpdate”参数,这种数据层用于web应用程序中,其中上下文在每次HTTP请求后都会消失