问题
Experts,
I'm getting this System.ArgumentException (An item with the same key has already been added) at the very first use of an entity framework function at runtime. The strange thing: If I'm using debug mode, it works fine. After compiling the same code without any changes made in release mode, it crashes immediatly with exception mentioned at top of this post.
Does anyone know more about such a strange behaviour? How can I fix it? I cannot roll out a debug version to my customer :(
Exception is thrown at this point:
try
{
var blub = context.ExecuteStoreQuery<int>(QueryString);
}
catch (Exception ex)
{
// ...
}
Stacktrace:
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:
First, make sure that you get the error even if you make a clean release build (I mean no binaries before running the build, source code only). If you don't, then fix your project setup (isolate debug and release build target folders for example).
If the error occurs only once and after that the app works fine, you must have some synchronization issues. Although it's quite strange, since metadata loading seems to be synchronized. But in any case, EF objects are not thread-safe. Do not use them in cross-thread scenarios.
If the above said doesn't help, then maybe the results of my research will help you. Here's an excerpt from the EF source code that relates to the error:
//----------------------------------------------------------------------
// <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);
}
}
}
// ...
}
}
The only dictionary used in the method is SessionData.TypesInLoading
. The key is a combination of RelationshipName
and RelationshipNamespaceName
. The funny thing is that a comment says they check for duplicated keys, but the method TryFindNullParametersInRelationshipAttribute
checks only that the properties of an attribute are not null
s. I suppose it's a bug.
But more important is that the described error must have occured because somewhere in your assemblies you have more than one EdmRelationshipAttribute
with equal combinations of RelationshipName
and RelationshipNamespaceName
properties. There might be various reasons:
- Incorrect EDMX file.
- Impropperly copied assemblies,
- Release configuration outputing it's build results to the same folder as Debug configuration,
- Errors in manual generation of EF mapping assemblies,
- etc.
Unfortunately, catching the name of the broken relationship will be very difficult. You'll probably have to set a breakpoint at System.Data.Metadata.Edm.ObjectItemAttributeAssemblyLoader.LoadRelationshipTypes()
and trace it in disassembly. I would say it will be nearly impossible because the Dictionary.Insert
method is about to be called many times.
If all these still don't suggest the solution, then you do need to prepare a repro of your issue and post it here. Otherwise nobody will be able to help.
来源:https://stackoverflow.com/questions/10206289/an-item-with-the-same-key-has-already-been-added-just-in-release-mode