Элемент с таким же ключом уже добавлен - просто в режиме релиза
Эксперты,
Я получаю это System.ArgumentException (элемент с тем же ключом уже был добавлен) при самом первом использовании функции структуры сущности во время выполнения. Странная вещь: если я использую режим отладки, он работает нормально. После компиляции того же кода без каких-либо изменений, сделанных в режиме релиза, он сразу падает, за исключением, упомянутым в начале этого поста.
Кто-нибудь знает больше о таком странном поведении? Как я могу это исправить? Я не могу выкатить отладочную версию для моего клиента:(
Исключение выдается в этой точке:
try
{
var blub = context.ExecuteStoreQuery<int>(QueryString);
}
catch (Exception ex)
{
// ...
}
Трассировки стека:
System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boo
lean add)
at System.Data.Metadata.Edm.ObjectItemAttributeAssemblyLoader.LoadRelationshi
pTypes()
at System.Data.Metadata.Edm.ObjectItemAttributeAssemblyLoader.LoadTypesFromAs
sembly()
at System.Data.Metadata.Edm.ObjectItemAssemblyLoader.Load()
at System.Data.Metadata.Edm.ObjectItemAttributeAssemblyLoader.Load()
at System.Data.Metadata.Edm.AssemblyCache.LoadAssembly(Assembly assembly, Boo
lean loadReferencedAssemblies, ObjectItemLoadingSessionData loadingData)
at System.Data.Metadata.Edm.AssemblyCache.LoadAssembly(Assembly assembly, Boo
lean loadReferencedAssemblies, ObjectItemLoadingSessionData loadingData)
at System.Data.Metadata.Edm.AssemblyCache.LoadAssembly(Assembly assembly, Boo
lean loadReferencedAssemblies, ObjectItemLoadingSessionData loadingData)
at System.Data.Metadata.Edm.AssemblyCache.LoadAssembly(Assembly assembly, Boo
lean loadReferencedAssemblies, ObjectItemLoadingSessionData loadingData)
at System.Data.Metadata.Edm.AssemblyCache.LoadAssembly(Assembly assembly, Boo
lean loadReferencedAssemblies, KnownAssembliesSet knownAssemblies, EdmItemCollec
tion edmItemCollection, Action`1 logLoadMessage, Object& loaderCookie, Dictionar
y`2& typesInLoading, List`1& errors)
at System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyFromCache(Object
ItemCollection objectItemCollection, Assembly assembly, Boolean loadReferencedAs
semblies, EdmItemCollection edmItemCollection, Action`1 logLoadMessage)
at System.Data.Metadata.Edm.MetadataWorkspace.ImplicitLoadAssemblyForType(Typ
e type, Assembly callingAssembly)
at System.Data.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](Stri
ng commandText, String entitySetName, MergeOption mergeOption, Object[] paramete
rs)
1 ответ
Во-первых, убедитесь, что вы получите ошибку, даже если вы делаете чистую сборку релиза (я имею в виду отсутствие двоичных файлов перед запуском сборки, только исходный код). Если вы этого не сделаете, то исправьте настройки проекта (например, изолируйте целевые папки отладки и выпуска сборки).
Если ошибка возникает только один раз, и после этого приложение работает нормально, у вас должны быть проблемы с синхронизацией. Хотя это довольно странно, поскольку загрузка метаданных кажется синхронизированной. Но в любом случае объекты EF не являются потокобезопасными. Не используйте их в многопоточных сценариях.
Если сказанное выше не поможет, то, возможно, вам помогут результаты моего исследования. Вот выдержка из исходного кода EF, которая относится к ошибке:
//----------------------------------------------------------------------
// <copyright file="ObjectItemAttributeAssemblyLoader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Reflection;
namespace System.Data.Metadata.Edm
{
/// <summary>
/// Class for representing a collection of items for the object layer.
/// Most of the implemetation for actual maintainance of the collection is
/// done by ItemCollection
/// </summary>
internal sealed class ObjectItemAttributeAssemblyLoader :
ObjectItemAssemblyLoader
{
// ...
/// <summary>
/// This method loads all the relationship type that this entity takes part in
/// </summary>
/// <param name="entityType"></param>
/// <param name="context"></param>
private void LoadRelationshipTypes()
{
foreach (EdmRelationshipAttribute roleAttribute in
SourceAssembly.GetCustomAttributes(typeof(EdmRelationshipAttribute), false /*inherit*/))
{
// Check if there is an entry already with this name
if (TryFindNullParametersInRelationshipAttribute(roleAttribute))
{
// don't give more errors for these same bad parameters
continue;
}
bool errorEncountered = false;
// return error if the role names are the same
if (roleAttribute.Role1Name == roleAttribute.Role2Name)
{
SessionData.EdmItemErrors.Add(new EdmItemError(
System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
null));
errorEncountered = true;
}
if (!errorEncountered)
{
AssociationType associationType = new AssociationType(
roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName,
roleAttribute.IsForeignKey, DataSpace.OSpace);
SessionData.TypesInLoading.Add(associationType.FullName, associationType);
TrackClosure(roleAttribute.Role1Type);
TrackClosure(roleAttribute.Role2Type);
// prevent lifting of loop vars
string r1Name = roleAttribute.Role1Name;
Type r1Type = roleAttribute.Role1Type;
RelationshipMultiplicity r1Multiplicity = roleAttribute.Role1Multiplicity;
AddTypeResolver(() =>
ResolveAssociationEnd(associationType, r1Name, r1Type, r1Multiplicity));
// prevent lifting of loop vars
string r2Name = roleAttribute.Role2Name;
Type r2Type = roleAttribute.Role2Type;
RelationshipMultiplicity r2Multiplicity = roleAttribute.Role2Multiplicity;
AddTypeResolver(() =>
ResolveAssociationEnd(associationType, r2Name, r2Type, r2Multiplicity));
// get assembly entry and add association type to the list of types in the assembly
Debug.Assert(!CacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
CacheEntry.TypesInAssembly.Add(associationType);
}
}
}
// ...
}
}
Единственный словарь, используемый в методе SessionData.TypesInLoading
, Ключ представляет собой комбинацию RelationshipName
а также RelationshipNamespaceName
, Самое смешное, что в комментарии говорится, что они проверяют дубликаты ключей, но метод TryFindNullParametersInRelationshipAttribute
проверяет только то, что свойства атрибута не null
s. Я полагаю, это ошибка.
Но более важно то, что описанная ошибка произошла, потому что где-то в ваших сборках у вас более одного EdmRelationshipAttribute
с равными комбинациями RelationshipName
а также RelationshipNamespaceName
свойства. Там могут быть разные причины:
- Неверный файл EDMX.
- Неправильно скопированные сборки,
- Выпуск конфигурации, вывод ее результатов сборки в ту же папку, что и конфигурация отладки,
- Ошибки в ручной генерации картографических сборок EF,
- и т.п.
К сожалению, поймать имя разорванных отношений будет очень сложно. Вам, вероятно, придется установить точку останова на System.Data.Metadata.Edm.ObjectItemAttributeAssemblyLoader.LoadRelationshipTypes()
и проследить это в разборке. Я бы сказал, что это будет почти невозможно, потому что Dictionary.Insert
метод должен вызываться много раз.
Если все это по-прежнему не предлагает решения, вам нужно подготовить репродукцию вашей проблемы и опубликовать ее здесь. Иначе никто не сможет помочь.