microsoft / Dynamics-AX-Integration

Dynamics AX Integration samples and demos.
286 stars 356 forks source link

ODataClient generation #38

Open Hesel opened 6 years ago

Hesel commented 6 years ago

When generating a new ODataClient (by changing the pointed to MetadatDocumentUri) I received the following error:

Running transformation: System.InvalidCastException: Unable to cast object of type 'Microsoft.OData.Edm.Csdl.CsdlSemantics.UnresolvedType' to type 'Microsoft.OData.Edm.IEdmCollectionType'.

No new ODataClient is create. I’m at a loss about what to do to fix this. Have tried updating the nugget packages and creating a new OData Client object in the project.

A single hit on google (https://stackoverflow.com/questions/32727022/unable-to-cast-object-of-type-microsoft-odata-edm-csdl-csdlsemantics-unresolved) mentions the same error, but the provided fix does not solve my issue.

dcs-kartik commented 6 years ago

Hi, We are getting same error.Did you get any solution for this?

tek9iner commented 6 years ago

Thia is a shot in the dark but are you trying to update the entities using VS 2017? I was having similar issuea regenerating the OData Client entities using VS 2017 but tried it on my other machine that has 2015 installed and it worked.

dcs-kartik commented 6 years ago

I have tried in VS2015 and VS2013 but got same error in both versions.

Hesel commented 6 years ago

I my case this issue was caused by some of the required Entities and Enums of the Metadata file missing. I had manually removed parts of the file, due to the size. Some of the Entities I had kept had internal references to some of those that were removed.

I found no easy way of figuring out which were missing. Ended up trial-and-erroring my way through those I had delete.

For my case, the required Entities and Enums were: (Since D365 has developed since this issue, these might no longer be the solution)

ENTITIES: DataManagementDefinitionGroup DataManagementDefinitionGroupDetail

ENUMS: DMFExecutionSummaryStatus AXType NoYes IntegrationActivityDataSource IntegrationActivityState IntegrationDirection EntityCategory DMFModule DMFProjectCategory DMFDefinitionGroupTemplateStatus DMFOperationType

Hope this helps

sk29110 commented 6 years ago

Is it possible to use postman against local development environment VM?

cmgerdes commented 6 years ago

I'm getting this same error and have tried stripping my file down to a single entity with no change in the result.

Has anyone been able to solve this? That this point I've tried the following (from memory): 1) Reinstalling OData template generator 2) updating OData nuget packages 3) downgrading OData nuget packages 4) removing all entities from the entity file but one 5) adding a try/catch around the code in GetClrTypeName that is causing the error so the resulting string would just be null (resulting .cs did not compile as you'd expect).

At this point I'm at a loss for what to do. Any help that people can offer would be appreciated.

Edit:

right now I'm going the route of catching the exception from the template code. This does result in non-compilable code but it seems like the issue is that the metadata it's processing is bad - I think D365 has a break in its metadata (I'm just using the base D365 install).

It appears that the entity that is broken as far as I can tell is WorkerTaxCodeParameters. This is based on the compile breaks that are present in the source once generated due to missing type names.

For reference, the code that I need to change in order to get the class to generate is:

ODataClient.ttinclude line 2683. Method GetClrTypeName. Basically you wrap the code in the else in a try/catch to avoid the exception so that it looks like below:

clrTypeName = "caughtExceptionName";
try
{
    IEdmCollectionType edmCollectionType = (IEdmCollectionType)edmType;
    IEdmTypeReference elementTypeReference = edmCollectionType.ElementType;
    IEdmPrimitiveType primitiveElementType = elementTypeReference.Definition as IEdmPrimitiveType;
    if (primitiveElementType != null)
    {
        clrTypeName = Utils.GetClrTypeName(primitiveElementType, clientTemplate);
    }
    else
    {
        IEdmSchemaElement schemaElement = (IEdmSchemaElement)elementTypeReference.Definition;
        clrTypeName = context.GetPrefixedFullName(schemaElement,
                context.EnableNamingAlias ? clientTemplate.GetFixedName(Customization.CustomizeNaming(schemaElement.Name)) : clientTemplate.GetFixedName(schemaElement.Name), clientTemplate);
    }    

    string collectionTypeName = isOperationParameter
                                ? clientTemplate.ICollectionOfTStructureTemplate
                                : (useDataServiceCollection
                                    ? (elementTypeReference.TypeKind() == EdmTypeKind.Entity
                                        ? clientTemplate.DataServiceCollectionStructureTemplate
                                        : clientTemplate.ObservableCollectionStructureTemplate)
                                    : clientTemplate.ObjectModelCollectionStructureTemplate);

    clrTypeName = string.Format(collectionTypeName, clrTypeName);
}
catch (Exception e)
{
    // Let exception run through
}

Edit 2: using the above approach I was able to go through and clean up the bad (non-compiling) C# code by manually removing the elements that didn't compile and I'm now able to consume the OData entities. This makes me more convinced that there is a break either in the D365 OOTB metadata or the current generation OData tool. I'm inclined towards the break being with the metadata from D365 itself.

dcs-kartik commented 6 years ago

Hello... The actual problem is in metadata. There is an Enum with name "ItemType" and also the Data Entity with same name "ItemType". Due to which the proxy file is not generated due to same name and giving the error.

The solution will be changing the name of Data Entity "ItemType" to "ItemTypes" EntityType Name="ItemTypes" and also do not forget to change the name in Entity Set in container. EntitySet Name="ItemTypes" EntityType="Microsoft.Dynamics.DataEntities.ItemTypes"

We have tested the same and it works fine.

cmgerdes commented 6 years ago

Ok, the errors I was seeing in the tax code entity must be the result of the itemType issue then

sitachaganti commented 6 years ago

could you share the metadata physical location

tek9iner commented 6 years ago

you can download it through your browser with a similar URL as below:

https://{YourEnvironmentRootURL}/data/$metadata

and then reference it in the ODataClient.tt file using a local path on your machine found on line 9 in the example: https://github.com/Microsoft/Dynamics-AX-Integration/blob/master/ServiceSamples/ODataUtility/ODataClient.tt

reidev275 commented 6 years ago

This may help someone... One of the entities in the resulting edmx file for us had a space in the end "myEntity " which caused the resulting .cs file to not compile because some of the method names were "myEntity OnChanging" which is an invalid method name in C#. The file was too large to load into VS so I opened with vim and edited the few lines by hand by removing the spaces.

markoars commented 5 years ago

I`ve also shrinked down the XML file to suite my needs. Basically D365 generated a file with a ton of entities and i needed several only. The 'cut' looked like this:

EntityType Name="MyNeededEntity1" /> // Include all entity types that you need

EnumType Name="XXXX" /> // Include all EnumTypes after the entity types

EntityContainer Name="Resources"> // Declare all entities that are in the XML here EntitySet Name="MyNeededEntity1" EntityType="Microsoft.Dynamics.DataEntities.MyNeededEntity1" /> /EntityContainer>

I`ve cut the XML file by using a a bunch of C# code.

The C# code for easily cutting the unwanted nodes looks like this:

    private void cutXMLFile()
    {
        bool foundOne = false;
       string ccConfigPath = "file path goes here";

        var doc = XDocument.Load(ccConfigPath);
        List<XElement> elements = doc.Element("RootElement").Elements().ToList();
   // I`ve created a new temporary XML file and put all nodes in a new root element named RootElement so it`s easier for me to get them

        foreach (var child in elements)
        {
             if (child.Name.ToString().Contains("EntityType") || child.Name.ToString().Contains("Action") )
             {
                     child.Remove();
                     foundOne = true;
             }
        }
    }