Но метод find "автоматически" обрезает мой ключ и в любом случае находит сохраненную сущность.
Я не верю, что обрезка происходит.Find
использует внутренний запрос SingleOrDefault
, другими словами: когда вы звоните ...
set.Find("ABCDE "); // including the trailing blank
... он использует этот запрос LINQ:
set.SingleOrDefault(key => key == "ABCDE "); // including the trailing blank
Проблеманаходится в базе данных, и результат зависит от порядка сортировки, языка, настроек прописных / строчных букв, акцентов и т. д. для ключевого поля string
в базе данных (например, nvarchar(10)
).
Например, если вы используете стандартный порядок сортировки Latin1_General
в SQL Server, ключи "ABCDE" и "ABCDE" (с завершающим пробелом) идентичны, вы не можете создать две строки, которыеиметь эти значения в качестве первичных ключей.Даже "ABCDE" и "abcde" идентичны (если вы не настроили различать заглавные и строчные буквы в SQL Server).
В то же времяэто означает, что также запросы для столбцов string
будут возвращать все совпадающие строки - в соответствии с порядком сортировки этого столбца в базе данных.Запрос для "ABCDE" с завершающим пробелом просто вернет запись с "ABCDE" без завершающего пробела.
До этого момента это "нормально"Поведение для всех запросов LINQ to Entities, которые содержат строки.
Теперь, как вы обнаружили, кажется, что ObjectContext не знает о настроенном порядке сортировки в базе данных и использует обычное сравнение строк .NET, гдестрока с и строка без завершающего пробела различаются.
Я не знаю, можно ли указать контексту использовать такое же сравнение строк, что и в базе данных.У меня есть некоторые сомнения, что это возможно, потому что мир .NET и мир реляционных баз данных слишком различны.Некоторые порядки сортировки могут быть специальными и доступны только в базе данных, а не в .NET вообще, и наоборот, возможно.Кроме того, существуют и другие базы данных, кроме SQL Server, которые должны поддерживаться Entity Framework, и эти базы данных могут иметь собственную систему порядка сортировки.
Для вашего конкретного случая - и, возможно, всегда, когда у вас есть ключи string
-возможное решение вашей проблемы - установить свойство ключа объекта для обновления на ключ объекта, возвращенного из базы данных:
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);
}
toUpdate
не должен быть присоединен к контексту, чтобы это работало.
Это ошибка?Я не знаю.По крайней мере, это является следствием несоответствия между .NET и миром реляционных баз данных и веской причиной, чтобы вообще не использовать string
ключевые столбцы / свойства.