Question Status

Verified
Tommy Skaue asked a question on 22 Mar 2013 1:26 PM

During import of a new updated version of a model, I am getting the error "An item with the same key has already been added".

Since the error is very generic, and does not really say what element is cause the problem, I made a quick C# Console project. The call stack reveals the following :

at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.ToDictionary[TSource,TKey](IEnumerable`1 source, Func`2 keySelector)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelContents.UpdatePaths()
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelStore.GetOrphansInclusiveParentPreDelete(SqlCommand command)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelStore.GetOrphansPreDeletePublisherCategory(SqlConnection connection, SqlTransaction transaction, CategoryType category, String publisher)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelImport.ModelImportReader.BuildOrphanListAndPrompt(SqlConnection connection, SqlTransaction transaction, Dictionary`2 tempIdToModelId, String publisher)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelImport.ModelImportReader.ResolveReplace(SqlConnection connection, SqlTransaction transaction)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelImport.ModelImportReader.ConflictResolve(SqlConnection connection, SqlTransaction transaction)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelImport.ModelImportReader.Import()
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.ModelImport.ModelImportReader.Import()
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.AxUtil.Import(Boolean verbose, Nullable`1 conflictTargetLayer)
at Microsoft.Dynamics.AX.Framework.Tools.ModelManagement.AxUtil.Import(AxUtilContext utilContext, AxUtilConfiguration utilConfig, Boolean verbose)
at ModelImportTest4.Program.Main(String[] args) in C:\Users\skautomm\documents\visual studio 2010\Projects\ModelImportTest4\ModelImportTest4\Program.cs:line 25
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

I reverse engineered the AxUtilLib and found the following body in the method throwing the error:

internal void UpdatePaths()
{
Dictionary<int, ModelElementInfo> dictionary = Enumerable.ToDictionary<ModelElementInfo, int>((IEnumerable<ModelElementInfo>) Enumerable.OrderBy<ModelElementInfo, int>((IEnumerable<ModelElementInfo>) this.Elements, (Func<ModelElementInfo, int>) (elmKey => elmKey.ElementHandle)), (Func<ModelElementInfo, int>) (keySelector => keySelector.ElementHandle));
foreach (ModelElementInfo modelElementInfo1 in dictionary.Values)
{
ModelElementInfo modelElementInfo2;
dictionary.TryGetValue(modelElementInfo1.ParentHandle, out modelElementInfo2);
if (modelElementInfo2 != null)
{
switch (modelElementInfo1.ElementType)
{
case ElementType.ClassHeader:
case ElementType.TableHeader:
modelElementInfo1.Path = ElementTypePath.GetListName(modelElementInfo2.ElementType) + "\\" + modelElementInfo2.Name + "\\Header";
continue;
case ElementType.VisualStudioProjectFolder:
case ElementType.VisualStudioProjectFile:
modelElementInfo1.Path = modelElementInfo2.Path + "\\" + modelElementInfo1.Name;
continue;
default:
string listName = ElementTypePath.GetListName(modelElementInfo1.ElementType);
if (!string.IsNullOrEmpty(listName))
{
modelElementInfo1.Path = modelElementInfo2.Path + "\\" + listName + "\\" + modelElementInfo1.Name;
continue;
}
else
{
modelElementInfo1.Path = modelElementInfo2.Path + "\\" + modelElementInfo1.Name;
continue;
}
}
}
else
modelElementInfo1.Path = ElementTypePath.GetListName(modelElementInfo1.ElementType) + "\\" + modelElementInfo1.Name;
}
this.elements = (IList<ModelElementInfo>) Enumerable.ToList<ModelElementInfo>((IEnumerable<ModelElementInfo>) Enumerable.OrderBy<ModelElementInfo, string>((IEnumerable<ModelElementInfo>) this.elements, (Func<ModelElementInfo, string>) (e => e.Path)));
}
}

That Linq-expression is throwing.

Since Microsoft has not been kind enough to let us have the Symbols for debugging, I am sort of searching for the needle in the haystack here. This model contains nearly three thousand elements. Using elimination method is tedious and time consuming. Obviously, I am dealing with an obsolete element handle most likely being modified in a higher layer but removed in the model I am importing.

Any bright ideas on how I would go around to find the duplicate element?

Tommy Skaue | Dynamics AX Developer from Norway | http://yetanotherdynamicsaxblog.blogspot.no/ | www.axdata.no

Reply
Verified Answer
Martin Dráb responded on 23 Mar 2013 4:36 AM

You can use Reflector to generate symbols and place breakpoints to almost any code - I've found it extremely useful. It's not free but you can use the trial version for your current task (and think later about whether the investment isn't worth for you or your company).

Martin "Goshoom" Dráb | Freelancer | Goshoom.NET Dev Blog

Reply
Daniel Weichsel responded on 22 Mar 2013 3:03 PM

Hi Tommy,

Although the exception is thrown where you have highlighted, could the problem be earlier in the method at the ToDictionary call due to delayed evaluation of the Linq expressions?  The stack trace seems to blame ToDictionary because of a duplicate key.

Have you tried using AxUtilLib in a C# program to gather all the element handles for your model file and the installed models?  Passing true as the second parameter to AxUtil.View will make the returned ModelContents object include the elements collection, including handles.

Reply
Tommy Skaue responded on 22 Mar 2013 4:43 PM

Hi Daniel.

I will have a look at that. Right now I am just utterly frustrated with the lack of auditing and logging of this "black box". I have tried to use SQL Profiler to find what element is causing the issue, but without the ability to step through AxUtilLib I am more or less forced to create a support ticket to Microsoft.

I will look into the View and see if I can get something out of it. :-)

Tommy Skaue | Dynamics AX Developer from Norway | http://yetanotherdynamicsaxblog.blogspot.no/ | www.axdata.no

Reply
Verified Answer
Martin Dráb responded on 23 Mar 2013 4:36 AM

You can use Reflector to generate symbols and place breakpoints to almost any code - I've found it extremely useful. It's not free but you can use the trial version for your current task (and think later about whether the investment isn't worth for you or your company).

Martin "Goshoom" Dráb | Freelancer | Goshoom.NET Dev Blog

Reply
Tommy Skaue responded on 23 Mar 2013 9:26 AM

Thanks for the tip, Martin! I have used the Reflector tool before, while it was free. 

So I downloaded the 14 day free trial and enabled debugging on AxUtilLib from within Visual Studio 2010. It worked like charm. I then used the Call Stack to narrow down where the error happened and step through the code. 

I found the exact location where the program throws, and found a list of elements where two of them had identical ElementHandle Id. This causes the Linq provider to throw, since ElementHandle was the key.

Since this was a form element, I just opened AX and deleted the entire form within the Developer Workspace. I then tried to install the model again, and voila - IT worked!

Here are some screenshots:

Problem solved! 

The developer behind this form says he don't have any exact reason for why this form bugged the installer, but he remembers copying the design from one form to another. This might have caused the issue. The form was developed on AX2012 RTM (probably CU3).

Tommy Skaue | Dynamics AX Developer from Norway | http://yetanotherdynamicsaxblog.blogspot.no/ | www.axdata.no

Reply
Brandon Ahmad responded on 23 Mar 2013 5:00 PM

Very, very helpful post. Maybe this will help me solve a LINQ problem that doesn't show up in LINQ to Entities, but shows up with LINQ to Dynamics with MVC.   Helpful indeed..  This is my favorite post over the last 2 months.  

Independent, Freelance Consultant and Dynamics Development Instructor

http://www.instructorbrandon.com

http://www.youtube.com/user/BrandonAhmad

Reply
Brandon Ahmad responded on 24 Mar 2013 6:41 PM

Gentlemen, I have one last question.  Do you know what the advantage of the reflector would be over WinDbg?  With WinDbg, we can download the symbols.  I'm just curious.  

Independent, Freelance Consultant and Dynamics Development Instructor

http://www.instructorbrandon.com

http://www.youtube.com/user/BrandonAhmad

Reply
Martin Dráb responded on 25 Mar 2013 12:09 AM

The answer is - they do completely different things. The main feature of Reflector is decompiling CIL to C#, so you'll get code in a high-level language. The feature for generating symbols allows you to debug running programs - this is basically the same as if you generate symbols from your own code. If you can download symbols, you don't need to generate them, of course, you'll just say Visual Studio where to find them - as you do in WinDbg.

WinDBg, on the other hand, is primarily used to analyze programs *after* they ran, therefore it allows you to analyze memory dumps and such things, but not to decompile CIL or generate symbols.

You could use Reflector to generate symbols and use them in WinDbg, if you needed to do a post-mortem analysis of some application without symbols.

Martin "Goshoom" Dráb | Freelancer | Goshoom.NET Dev Blog

Reply