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
263 stars 182 forks source link

FindReflectedAttributeType Object reference not set to an instance of an object. #560

Open ioanacro opened 3 years ago

ioanacro commented 3 years ago

Hi, I have this test that is joining Account entity with my custom entity Project. When I add a condition on account I get Object reference not set to an instance of an object. error in FakeXrmEasy.XrmFakedContext.FindReflectedAttributeType when calling RetrieveMultiple method.

    [Test]
    public void TestStandardEntities()
    {
        this.context = new XrmFakedContext();
        var accountId = Guid.NewGuid();
        var AccountToBeFound = new Account { Id = accountId, Name = "TestAccount", AccountId = accountId };

        var ProjectToBeFound = new project
        {
            name = "TestProject",
            projectId = Guid.NewGuid(),
            projectcode = "someCode",
            accountid = AccountToBeFound.ToEntityReference()
        };

        this.context.Initialize(new List<Entity> { AccountToBeFound, ProjectToBeFound });

        this.service = this.context.GetOrganizationService();

        var xml = @"<fetch count='10' distinct='true' page='1'>
                            <entity name='project'>
                                <attribute name='projectid' />
                                   <filter type='or'>
                                         <condition entityname='account' attribute='name' operator='like' value='TestAccount%' />
                                   </filter>
                                <link-entity name='account' from='accountid' to='accountid'>
                                </link-entity>
                            </entity>
                         </fetch>";

        var response = this.service.RetrieveMultiple(new FetchExpression(xml));

        response.Entities.Count().Should().Be(1);
    }

This query is working fine on the real CRM database. What object is null in this case?

Using 1.57.0.0 version

Another thing I noticed is if I change my condition to use another account attribute like accountratingcode

then I get System.Exception : XrmFakedContext.FindReflectedAttributeType: Attribute accountratingcode not found for type ...Models.project

Looks like the library is looking in my custom entity for those attributes.

jordimontana82 commented 3 years ago

Hi, latest version is 1.57.1, not sure if you tried that one and have the same issue there?

jordimontana82 commented 3 years ago

Also, could you check if the new attribute was generated (assuming you're generating early bound entities with crmsvcutil?)

ioanacro commented 3 years ago

I've update to latest version and re-run EarlyBound generator tool from XRMToolbox and the problem still persists.

jordimontana82 commented 3 years ago

Thanks @ioanacro ,

If you could please post the entire stack trace and the generated entities involved in this test, so the community might help? Otherwise it's going to be very difficult to reproduce.

Thanks!

ioanacro commented 3 years ago

Hi @jordimontana82 ,

ProjectTest.txt Competitor.txt test.txt

I've attached the generated entities, I used new ones only for testing purposes.

My guess from the 2 errors above is that earlyBoundType in FindReflectedAttributeType is wfp_ProjectClass instead of Competitor and propertyInfo ends up being null since everything is substracted from attributeName="name".

This is the stack trace: System.NullReferenceException : Object reference not set to an instance of an object. at FakeXrmEasy.XrmFakedContext.FindReflectedAttributeType(Type earlyBoundType, String sEntityName, String attributeName) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.GetConditionExpressionValueCast(String value, XrmFakedContext ctx, String sEntityName, String sAttributeName, ConditionOperator op) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.ToConditionExpression(XElement elem, XrmFakedContext ctx) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.<>cDisplayClass21_0.b3(XElement el) at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.ToFilterExpression(XElement elem, XrmFakedContext ctx) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.<>cDisplayClass16_0.b1(XElement el) at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source) at FakeXrmEasy.Extensions.FetchXml.XmlExtensionsForFetchXml.ToCriteria(XDocument xlDoc, XrmFakedContext ctx) at FakeXrmEasy.XrmFakedContext.TranslateFetchXmlDocumentToQueryExpression(XrmFakedContext context, XDocument xlDoc) at FakeXrmEasy.FakeMessageExecutors.RetrieveMultipleRequestExecutor.Execute(OrganizationRequest req, XrmFakedContext ctx) at FakeXrmEasy.XrmFakedContext.<>cDisplayClass155_0.b0(QueryBase req) at FakeXrmEasy.XrmFakedContext.<>cDisplayClass155_0.b2(QueryBase req) at FakeItEasy.Configuration.BuildableCallRule.Apply(IInterceptedFakeObjectCall fakeObjectCall) in C:\projects\fakeiteasy\src\FakeItEasy\Configuration\BuildableCallRule.cs:line 97 at FakeItEasy.Core.FakeManager.ApplyBestRule(IInterceptedFakeObjectCall fakeObjectCall) in C:\projects\fakeiteasy\src\FakeItEasy\Core\FakeManager.cs:line 261 at FakeItEasy.Core.FakeManager.FakeItEasy.Core.IFakeCallProcessor.Process(InterceptedFakeObjectCall fakeObjectCall) in C:\projects\fakeiteasy\src\FakeItEasy\Core\FakeManager.cs:line 173 at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.ObjectProxy.RetrieveMultiple(QueryBase query)

jordimontana82 commented 3 years ago

In the FetchXml attached it seems you are trying to retrieve an attribute using the name of the entity instead of the attribute name:

 <attribute name='{wfp_ProjectTest.EntityLogicalName}' />

@ioanacro Could you remove this bit to see if that works?

jordimontana82 commented 3 years ago

imagen

ioanacro commented 3 years ago

ou are trying to retrieve an attribute using the name of the entity instead of the attribute name:

Hey, I still get 'Object reference not set to an instance of an object.' error with that line fixed or removed

BetimBeja commented 3 years ago
[Test]
public void TestStandardEntities()
{
    this.context = new XrmFakedContext();
    var accountId = Guid.NewGuid();
    var competitor = new Competitor { Id = accountId, Name = "something to look for" };

    var project = new wfp_ProjectTest
    {
        wfp_Competitor = competitor.ToEntityReference(),
        wfp_ProjectTestId = Guid.NewGuid()
    };

    this.context.Initialize(new List<Entity> { competitor, project });

    this.service = this.context.GetOrganizationService();

    var xml = $@"<fetch top='50' >
                  <entity name='{wfp_ProjectTest.EntityLogicalName}' >
                    <attribute name='{wfp_ProjectTest.EntityLogicalName}' />
                    <filter>
                      <condition entityname='{Competitor.EntityLogicalName}' attribute='name' operator='like' value='%something%' />
                    </filter>
                    <link-entity name='competitor' from='competitorid' to='wfp_appointment' />
                  </entity>

                  <entity name='{wfp_ProjectTest.EntityLogicalName}' >
                    <attribute name='{wfp_ProjectTest.Fields.wfp_ProjectTestId}' />
                    <filter>
                      <condition entityname='{Competitor.EntityLogicalName}' attribute='{Competitor.Fields.Name}' operator='like' value='%something%' />
                    </filter>
                    <link-entity name='{Competitor.EntityLogicalName}' from='{Competitor.Fields.CompetitorId}' to='{wfp_ProjectTest.Fields.wfp_Competitor}' />
                  </entity>
                </fetch>";

    var response = this.service.RetrieveMultiple(new FetchExpression(xml));

    response.Entities.Count().Should().Be(1);
}

I don't think that specifying twice your entity node in the FetchXML is supported in FakeXrmEasy

jordimontana82 commented 3 years ago

Good spot @BetimBeja That wasn't in the orignal bug at the top and I missed it @ioanacro .

Is that supported in the FetchXml schema even? I never seen a fetchXml with two entity nodes myself....

ioanacro commented 3 years ago

you are right! Bad copy&paste from the initial code:

I've modified the test to :
[Test] public void TestStandardEntities() { this.context = new XrmFakedContext(); var accountId = Guid.NewGuid(); var competitor = new Competitor { Id = accountId, Name = "something to look for" };

        var project = new wfp_ProjectTest
        {
            wfp_Competitor = competitor.ToEntityReference(),
            wfp_ProjectTestId = Guid.NewGuid()
        };

        this.context.Initialize(new List<Entity> { competitor, project });

        this.service = this.context.GetOrganizationService();

        var xml = $@"<fetch top='50' >
                      <entity name='{wfp_ProjectTest.EntityLogicalName}' >
                        <attribute name='{wfp_ProjectTest.Fields.wfp_ProjectTestId}' />
                        <filter>
                          <condition entityname='{Competitor.EntityLogicalName}' attribute='{Competitor.Fields.Name}' operator='like' value='%something%' />
                        </filter>
                        <link-entity name='{Competitor.EntityLogicalName}' from='{Competitor.Fields.CompetitorId}' to='{wfp_ProjectTest.Fields.wfp_Competitor}' />
                      </entity>
                    </fetch>";

        var response = this.service.RetrieveMultiple(new FetchExpression(xml));

        response.Entities.Count().Should().Be(1);
    }

But still getting the error

BetimBeja commented 3 years ago

@jordimontana82 I think I found the bug. https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Shared/XrmFakedContext.Queries.cs#L183-L194 here in this part, there is a check, if the string ends with name, which is valid because it is looking for the name property, but it doesn't find it in the wfp_ProjectTest entity and throws with a null reference in this line

ioanacro commented 3 years ago

@jordimontana82 I think I found the bug. https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Shared/XrmFakedContext.Queries.cs#L183-L194

here in this part, there is a check, if the string ends with name, which is valid because it is looking for the name property, but it doesn't find it in the wfp_ProjectTest entity and throws with a null reference in this line

Hi @BetimBeja,

But why it looks in wfp_projectTest for the attribute name? the condition is on Competitor: <condition entityname='{Competitor.EntityLogicalName}' attribute='{Competitor.Fields.Name}'...>

dkanaev commented 3 years ago

@jordimontana82 I faced the same problem, in my case, the problem is in this line image

BetimBeja commented 3 years ago

image https://docs.microsoft.com/en-us/dotnet/api/system.type.basetype?view=net-5.0#remarks I think you are creating Early Bound Entities with object returning properties...

dkanaev commented 3 years ago

@BetimBeja yes, I created Early Bound Entities. But, it is not an object type.

[AttributeLogicalName("rr_unitcategories")] public virtual IEnumerable rr_UnitCategories { get; set; }

This property was autogenerated for multi-select optionset value

BetimBeja commented 3 years ago

the next paragraph tells you that the same is valid for Interfaces too image What is the benefit of having the IEnumerable instead of a concrete class in this particular case?

dkanaev commented 3 years ago

The point is I use EarlyBoundGenerator to generate this code and at the moment I did not find how to configure it for generating class instead of interface

jordimontana82 commented 3 years ago

@ioanacro Which early bound generator did you use? Just to check if it's really the same issue...

ioanacro commented 2 years ago

Hey, I'm also using EarlyBoundGenerator