EF 4.1 code首先错误在查找/更新。任何解决方法吗?它应该被报道?解决方法、错误、EF、code

2023-09-05 01:20:49 作者:英雄有梦.°没死别停

我发现在C首先EF 4.1 $ C $一个pretty的讨厌的错误。假设我们有这块code从上下文中检索实体,然后用新的值更新:

 公共T更新< T>(T更新所),其中T:类
        {
            System.Data.Objects.ObjectContext的ObjectContext =((System.Data.Entity.Infrastructure.IObjectContextAdapter)_context).ObjectContext;
            System.Data.Objects.ObjectSet< T>设置= objectContext.CreateObjectSet< T>();
            IEnumerable的<字符串>键名= set.EntitySet.ElementType
                                                        .KeyMembers
                                                        。选择(K => k.Name);
            VAR类型= ty​​peof运算(T);
            VAR值= keyNames.Select(C => type.GetProperty(三).GetValue(更新所使用,NULL))。的ToArray();
            无功电流= _context.Set< T>()查找(值);
            如果(电流!= NULL)
            {
                _context.Entry(电流).CurrentValues​​.SetValues​​(更新所使用);
            }
            返回电流;
        }
 

现在假设我的实体已经是一个字符串中的一个关键属性。

工作场景:存储实体具有关键的ABCDE我的更新所使用的实体有相同的密钥ABCDE:一切正常。

错误的场景:存储实体具有关键的ABCDE我的更新所使用的实体有重点ABCDE(注意最后一个字母后面的空格)

这两个键是确有不同。但find方法自动地修剪我的钥匙,发现所存储实体反正。这将是很好的,如果它没有打破setValues​​方法将:由于存储的密钥和新的密钥都不同,我得到(正确地)以下内容:

  

属性'身份证'是对象的关键信息的一部分,并不能   修改

由于,是不同的,它会尝试更新它,因为它是一个关键属性不能被更新,所以整个事情失败并引发。

我认为是查找的方法不应该自动地修剪键值(或不管它是在内部做使两个不同的字符串出现相同的)。在第二种情况下,查找的方法应该返回null。

你必须知道的EF知识和经验

现在两件事情:我怎么解决这个临时,我在哪里可以报告这个错误,因为我无法找到一个正式的地方报告错误

感谢。

编辑:这里报告错误:https://connect.microsoft.com/VisualStudio/feedback/details/696352/ef-$c$c-first-4-1-find-update-bug

解决方案   

但find方法自动地修剪我的钥匙,并找到存储   实体反正。

我不'相信修整情况。 查找使用的SingleOrDefault 查询内部,换句话说:当你打电话......

  set.Find(ABCDE); //包括结尾的空白
 

...它使用此LINQ查询:

  set.SingleOrDefault(键=>关键==ABCDE); //包括结尾的空白
 

现在的问题是在数据库和结果取决于的排序顺序,语言,设​​置资本/小写字母,口音等,为你的字符串重点领域数据库(为nvarchar(10)为例)。

有关,如果你使用一个标准的 Latin1_General 排序顺序在SQL Server实例中键ABCDE和ABCDE(尾随空白)是相同的,你无法创建两个具有这些值作为主键列。即使ABCDE和ABCDE是相同的(如果你没有设置区分在SQL Server资本和小写字母)。

同时,这意味着还查询了字符串列将返回所有匹配的行 - 相对于在数据库中列的排序顺序相匹配。对于ABCDE查询尾随只返回一个创纪录的空白ABCDE无拖尾的空白。

至此这是涉及弦Entities查询正常的行为对于所有的LINQ

现在,似乎 - 当你发现 - 该ObjectContext的不知道在数据库中配置的排序顺序,并使用普通的.NET字符串比较,其中一个字符串和一个字符串没有结尾的空白是不同

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

有关您的具体情况 - 也许永远当你有字符串键 - 您的问题的一个可能的解决将是实体的关键属性设置为更新的关键的对象从数据库返回的:

  toUpdate.Id = current.Id;
_context.Entry(电流).CurrentValues​​.SetValues​​(更新所使用);
 

或者更一般地在你的code的范围内:

  // ...
无功电流= _context.Set< T>()查找(值);
如果(电流!= NULL)
{
    的foreach(在键名VAR注册表项目)
    {
        VAR到currentValue = type.GetProperty(注册表项目).GetValue(电流,NULL);
        type.GetProperty(注册表项目).SetValue(更新所使用,到currentValue,NULL);
    }
    _context.Entry(电流).CurrentValues​​.SetValues​​(更新所使用);
}
 

更新所使用不得附加到上下文得到这个工作。

这是一个错误?我不知道。至少它是.NET和关系型数据库的世界和一个很好的理由,以避免字符串键列/摆在首位的属性。

不匹配的结果

I've found a pretty nasty bug in EF 4.1 Code First. Assume we have this piece of code to retrieve an entity from the context and then update it with new values:

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

Now assume that my entity has a single key property which is a string.

Working scenario: stored entity has key "ABCDE" and my toUpdate entity has same key "ABCDE": everything works fine.

Bug scenario: stored entity has key "ABCDE" and my toUpdate entity has key "ABCDE " (notice the space after the last letter).

The two keys are indeed different. But the find method "automagically" trims my key and finds the stored entity anyway. This would be good, if it didn't break the SetValues method: since the stored key and the new key are different, I get (rightly) the following:

The property 'Id' is part of the object's key information and cannot be modified.

Because, being different, it tries to update it, and since it's a key property it cannot be updated, so the whole thing fails and throws.

What I think is that the "Find" method shouldn't automagically trim the key values (or whatever it is doing internally to make the two different strings appear the same). In the second scenario, the "Find" method should return null.

Now two things: how do I workaround this temporarily, and where can I report this bug, because I couldn't find an official place to report the bug.

Thanks.

EDIT: Reported the bug here: https://connect.microsoft.com/VisualStudio/feedback/details/696352/ef-code-first-4-1-find-update-bug

解决方案

But the find method "automagically" trims my key and finds the stored entity anyway.

I dont' believe that trimming happens. Find uses a SingleOrDefault query internally, in other words: When you call...

set.Find("ABCDE "); // including the trailing blank

...it uses this LINQ query:

set.SingleOrDefault(key => key == "ABCDE "); // including the trailing blank

The problem is in the database and the result depends on the sort order, language, settings for capital/small letters, accents, etc. for your string key field in the database (nvarchar(10) for example).

For example if you use a standard Latin1_General sort order in SQL Server the key "ABCDE" and "ABCDE " (with trailing blank) are identical, you cannot create two rows which have these values as primary keys. Even "ABCDE" and "abcde" are identical (if you don't setup to distinguish capital and small letters in SQL Server).

At the same time this means that also queries for string columns will return all matching rows - matching with respect to the sort order of that column in the database. The query for "ABCDE " with trailing blank will just return a record with "ABCDE" without the trailing blank.

Up to this point this is "normal" behaviour for all LINQ to Entities queries which involve strings.

Now, it seems - as you found - that the ObjectContext doesn't know about the configured sort order in the database and uses the normal .NET string comparison where a string with and a string without trailing blank are different.

I don't know if it's possible to tell the context to use the same comparison of strings as the database. I have some doubt that this is possible because the .NET world and the relational database world are too different. Some sort orders might be special and only available in the database and not in .NET at all and vice versa perhaps. In addition there are also other databases than SQL Server which must be supported by Entity Framework and those databases might have their own sort order system.

For your specific case - and perhaps always when you have string keys - a possible fix of your problem would be to set the key property of the entity to update to the key of the object returned from the database:

toUpdate.Id = current.Id;
_context.Entry(current).CurrentValues.SetValues(toUpdate);

Or more generally in the context of your code:

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

toUpdate must not be attached to the context to get this working.

Is this a bug? I don't know. At least it is a consequence of a mismatch between .NET and relational database world and a good reason to avoid string key columns/properties in the first place.