jordimontana82 / fake-xrm-easy

The testing framework for Dynamics CRM and Dynamics 365 which runs on an In-Memory context and deals with mocks or fakes for you
https://dynamicsvalue.com/get-started/overview?source=git
Other
262 stars 182 forks source link

Specified Cast is not valid #116

Closed don-matese closed 7 years ago

don-matese commented 7 years ago

When testing a method from a third party I get the error "specified cast is not valid" . In all honesty I am not sure if the problem is the query or the FakeXrmEasy test.

you can also read more here http://stackoverflow.com/questions/41978580/specified-cast-invalid

Included code below


        private List<Entity> SetupContactTests()
        {
            var entityList = new List<Entity>();

            var company = new Account() { AccountId = new Guid("0C2C9B96-0DC5-47F4-B9DD-2E6B765823B3"), Name = "Company XYZ" };
            var company2 = new Account() { AccountId = new Guid("190B49D8-119E-4FDC-84B2-0485754CAFE2"), Name = "Company ABC" };

            entityList.Add(new Contact() { Id = new Guid("B6B4B46B-3209-4A8F-8FC8-A16F27CC44F3"), FirstName = "Contact XYZ 1", LastName = "Surname", ParentCustomerId = new EntityReference(company.LogicalName, new Guid("0C2C9B96-0DC5-47F4-B9DD-2E6B765823B3")), EMailAddress1 = "1@xyz.com" });
            entityList.Add(new Contact() { Id = new Guid("57AC385E-1B0D-417D-B030-3307BEB13A2B"), FirstName = "Contact XYZ 2", LastName = "Surname", ParentCustomerId = new EntityReference(company.LogicalName, new Guid("0C2C9B96-0DC5-47F4-B9DD-2E6B765823B3")), EMailAddress1 = "1@xyz.com" });
            entityList.Add(new Contact() { Id = new Guid("E396BEF5-F1A9-4100-A838-02E942AABF56"), FirstName = "Contact XYZ 3", LastName = "Surname", ParentCustomerId = new EntityReference(company.LogicalName, new Guid("0C2C9B96-0DC5-47F4-B9DD-2E6B765823B3")), EMailAddress1 = "1@xyz.com" });

            entityList.Add(new Contact() { Id = new Guid("FC2D1344-3468-4E54-B07F-27DE63BFA899"), FirstName = "Contact ABC 1", LastName = "Surname", ParentCustomerId = new EntityReference(company2.LogicalName, new Guid("190B49D8-119E-4FDC-84B2-0485754CAFE2")), EMailAddress1 = "1@abc.com" });
            entityList.Add(new Contact() { Id = new Guid("BA9BE182-21A0-47F3-8C5C-4A7B36D765C9"), FirstName = "Contact ABC 2", LastName = "Surname", ParentCustomerId = new EntityReference(company2.LogicalName, new Guid("190B49D8-119E-4FDC-84B2-0485754CAFE2")), EMailAddress1 = "1@abc.com" });
            entityList.Add(new Contact() { Id = new Guid("9D78DDC4-46CD-45A3-B806-59ED27E6F91A"), FirstName = "Contact ABC 3", LastName = "Surname", ParentCustomerId = new EntityReference(company2.LogicalName, new Guid("190B49D8-119E-4FDC-84B2-0485754CAFE2")), EMailAddress1 = "1@abc.com" });

            company.PrimaryContactId = new EntityReference(entityList[2].LogicalName, new Guid("B6B4B46B-3209-4A8F-8FC8-A16F27CC44F3"));
            company2.PrimaryContactId = new EntityReference(entityList[2].LogicalName, new Guid("FC2D1344-3468-4E54-B07F-27DE63BFA899"));

            entityList.Add(company);
            entityList.Add(company2);

            return entityList;
        }

        private QueryExpression CreateBrokenTestQuery(string contactId)
        {
            QueryExpression query = new QueryExpression();

            query.PageInfo = new PagingInfo();
            query.PageInfo.Count = 100;
            query.PageInfo.PageNumber = 100;
            query.PageInfo.PagingCookie = null;

            // Setup the query for the contact entity
            query.EntityName = Contact.EntityLogicalName;

            // Specify the columns to retrieve
            query.ColumnSet = new ColumnSet(true);

            query.Criteria = new FilterExpression();
            query.Criteria.FilterOperator = LogicalOperator.And;

            FilterExpression filter = new FilterExpression(LogicalOperator.And);

            // Create the link from the contact to the account entity
            LinkEntity linkAccount = new LinkEntity();
            linkAccount.JoinOperator = JoinOperator.Inner;
            linkAccount.LinkFromEntityName = Contact.EntityLogicalName;
            linkAccount.LinkFromAttributeName = Contact.AttributeNames.ParentCustomerId;
            linkAccount.LinkToEntityName = Account.EntityLogicalName;
            linkAccount.LinkToAttributeName = Account.AttributeNames.Id;
            linkAccount.Columns = new ColumnSet(true);
            linkAccount.EntityAlias = "accountItem";

#region this is the broken additional field condition
            linkAccount.LinkCriteria = new FilterExpression();
            linkAccount.LinkCriteria.FilterOperator = LogicalOperator.And;

            // Create the primary contact condition
            ConditionExpression condition3 = new ConditionExpression();
            condition3.AttributeName = Account.AttributeNames.PrimaryContactId;
            condition3.Operator = ConditionOperator.Equal;
            condition3.Values.Add(new Guid(contactId));

            linkAccount.LinkCriteria.Conditions.Add(condition3);
#endregion 
            query.LinkEntities.Add(linkAccount);
            return query;
        }

        private QueryExpression CreateWorkingTestQuery(string contactId)
        {
            QueryExpression query = new QueryExpression();

            query.PageInfo = new PagingInfo();
            query.PageInfo.Count = 100;
            query.PageInfo.PageNumber = 100;
            query.PageInfo.PagingCookie = null;

            // Setup the query for the contact entity
            query.EntityName = Contact.EntityLogicalName;

            // Specify the columns to retrieve
            query.ColumnSet = new ColumnSet(true);

            query.Criteria = new FilterExpression();
            query.Criteria.FilterOperator = LogicalOperator.And;

            FilterExpression filter = new FilterExpression(LogicalOperator.And);

            // Create the link from the contact to the account entity
            LinkEntity linkAccount = new LinkEntity();
            linkAccount.JoinOperator = JoinOperator.Inner;
            linkAccount.LinkFromEntityName = Contact.EntityLogicalName;
            linkAccount.LinkFromAttributeName = Contact.AttributeNames.ParentCustomerId;
            linkAccount.LinkToEntityName = Account.EntityLogicalName;
            linkAccount.LinkToAttributeName = Account.AttributeNames.AccountId;
            linkAccount.Columns = new ColumnSet(true);
            linkAccount.EntityAlias = "accountItem";

            linkAccount.LinkCriteria = new FilterExpression();
            linkAccount.LinkCriteria.FilterOperator = LogicalOperator.And;

#region this is the working additional field reference
            // Create the primary contact condition
            LinkEntity linkContact = new LinkEntity(Account.EntityLogicalName, Contact.EntityLogicalName, Account.AttributeNames.PrimaryContactId, Contact.AttributeNames.Id, JoinOperator.Inner);
            linkContact.Columns = new ColumnSet(true);
            linkContact.EntityAlias = "primaryContact";

            linkContact.LinkCriteria = new FilterExpression();
            linkContact.LinkCriteria.FilterOperator = LogicalOperator.And;

            linkContact.LinkCriteria.Conditions.Add(new ConditionExpression(Contact.AttributeNames.Id, ConditionOperator.Equal, new Guid(contactId)));
            linkAccount.LinkEntities.Add(linkContact);
#endregion
            query.LinkEntities.Add(linkAccount);
            return query;
        }

        [Fact]
        public void __QueryExpression_Test_CodeBased__Contact_Account_Contact_Broken()
        {
            var ctx = new XrmFakedContext();
            var service = ctx.GetOrganizationService();

            ctx.Initialize(SetupContactTests());

            EntityCollection ec = service.RetrieveMultiple(CreateBrokenTestQuery("B6B4B46B-3209-4A8F-8FC8-A16F27CC44F3"));
            Assert.Equal(3, ec.Entities.Count);
        }

        [Fact]
        public void __QueryExpression_Test_CodeBased_Contact_Account_Contact_Working()
        {
            var ctx = new XrmFakedContext();
            var service = ctx.GetOrganizationService();

            ctx.Initialize(SetupContactTests());

            EntityCollection ec = service.RetrieveMultiple(CreateWorkingTestQuery("B6B4B46B-3209-4A8F-8FC8-A16F27CC44F3"));
            Assert.Equal(3, ec.Entities.Count);
        }
jordimontana82 commented 7 years ago

Hi @don-matese ,

Just checked in some code to reproduce your issue and both queries run fine. This is normally related to the way crmsvcutil generates the early bound types, cause there might be difference across different versions of the crmsvcutil. For instance, closed issue #114 was exactly the same thing, a crmsvcutil was generating enums as nullable ints, instead of the previous tested cases with Enums or OptionSetValue for example.

Could you post what types do you have in your generated classes for the properties involved in the queries above?

This is the applied fix for #114, could you add a new case there maybe with your specific types, and submit a PR?

Cheers

don-matese commented 7 years ago

Hi Jordi,

They are all EntityReference fields declared as follows

[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("primarycontactid")] public Microsoft.Xrm.Client.CrmEntityReference PrimaryContactId { get { return this.GetAttributeValue("primarycontactid"); } set {

this.SetAttributeValue("PrimaryContactId", "primarycontactid", value); } }

On 2 February 2017 at 09:06, Jordi notifications@github.com wrote:

Hi @don-matese https://github.com/don-matese ,

Just checked in some code to reproduce your issue and both queries run fine. This is normally related to the way crmsvcutil generates the early bound types, cause there might be difference across different versions of the crmsvcutil. For instance, closed issue #114 https://github.com/jordimontana82/fake-xrm-easy/issues/114 was exactly the same thing, a crmsvcutil was generating enums as nullable ints, instead of the previous tested cases with Enums or OptionSetValue for example.

Could you post what types do you have in your generated classes for the properties involved in the queries above?

Cheers

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jordimontana82/fake-xrm-easy/issues/116#issuecomment-276903295, or mute the thread https://github.com/notifications/unsubscribe-auth/ABl8KHx-k1RhKbmCtVTtR5l_Ky-thLTHks5rYZyDgaJpZM4L0El_ .

jordimontana82 commented 7 years ago

Just one thing then... I replaced your attributenames definitions by lowercase as the AttributeNames wasn't in your sample, maybe you aren't using lowercase names? Are you on latest version 1.20.1? Cause those examples run fine without exceptions :)

jordimontana82 commented 7 years ago

Mmm runs fine locally, fails on appveyor, but probably related to referenced SDK version. Will double check again

don-matese commented 7 years ago

I have the latest version, given one of the queries works and the other doesn't I think I am more inclined to believe either the code I inherited is flawed (likely) or the structure of the query is unexpected in the QueryExpression handler in FakeXrmEasy. Either way, I suspect its the way the Query in CreateBrokenTestQuery is constructed thats at the root of the issue - thanks for your time on this though.

Mauro

On 2 February 2017 at 09:20, Jordi notifications@github.com wrote:

Mmm runs fine locally, fails on appveyor, but probably related to referenced SDK version. Will double check again

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jordimontana82/fake-xrm-easy/issues/116#issuecomment-276906280, or mute the thread https://github.com/notifications/unsubscribe-auth/ABl8KHITAOPX-dMJjDRP3TWGf9PwMUuCks5rYZ_zgaJpZM4L0El_ .

don-matese commented 7 years ago

Apologies Jordi

Having looked at the code a bit more closely, while the Attributes I am filtering on are indeed EntityReferences, other fields are declared as nullable

Your entity code for example has this attribute in Account

/// <summary>
    /// Select a category to indicate whether the customer account is

standard or preferred. ///

[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("accountcategorycode")] public Microsoft.Xrm.Sdk.OptionSetValue AccountCategoryCode { get { return this.GetAttributeValue("accountcategorycode"); } set { this.OnPropertyChanging("AccountCategoryCode"); this.SetAttributeValue("accountcategorycode", value); this.OnPropertyChanged("AccountCategoryCode"); } }

Whereas mine does the following

    /// <summary>
    /// Drop-down list for selecting the category of the account.
    /// </summary>

[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("accountcategorycode")] public System.Nullable AccountCategoryCode { get { return this.GetAttributeValue<System.Nullable>("accountcategorycode"); } set {

this.SetAttributeValue("AccountCategoryCode", "accountcategorycode", value); } }

Your code was generated using CrmSvcUtil [System.CodeDom.Compiler.GeneratedCodeAttribute("CrmSvcUtil", "7.0.0000.3543")] mine was [System.CodeDom.Compiler.GeneratedCodeAttribute("CrmSvcUtil", "5.0.9688.1046")]

Does your fix then mean I need to recompile my Entities Classes using a newer CrmSvcUtil?

On 2 February 2017 at 09:26, Mauro Masucci mauro.masucci@gmail.com wrote:

I have the latest version, given one of the queries works and the other doesn't I think I am more inclined to believe either the code I inherited is flawed (likely) or the structure of the query is unexpected in the QueryExpression handler in FakeXrmEasy. Either way, I suspect its the way the Query in CreateBrokenTestQuery is constructed thats at the root of the issue - thanks for your time on this though.

Mauro

On 2 February 2017 at 09:20, Jordi notifications@github.com wrote:

Mmm runs fine locally, fails on appveyor, but probably related to referenced SDK version. Will double check again

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jordimontana82/fake-xrm-easy/issues/116#issuecomment-276906280, or mute the thread https://github.com/notifications/unsubscribe-auth/ABl8KHITAOPX-dMJjDRP3TWGf9PwMUuCks5rYZ_zgaJpZM4L0El_ .

jordimontana82 commented 7 years ago

Wow! that looks like a bug in the crmsvcutil version you are using (which is highly likely, for instance, latest developer toolkit for 365 beta generates types which don't even build ).

It is weird that one property should be retrieved as a Nullable in the getter but set as an OptionSetValue in the setter, isn't it? Looks like a bug in that version of crmsvcutil :)

Issue #114 was sorted with this version: 5.0.9690.2165

Could you try with that one?

jordimontana82 commented 7 years ago

Hi Mauro, did that work with crmsvcutil from latest 2011 SDK?

jordimontana82 commented 7 years ago

Hi Mauro, closing this one. If you still are having issues please reopen it.