jordimontana82/fake-xrm-easy

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

ioanacro opened this issue · 20 comments

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.

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

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

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

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!

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.<>c__DisplayClass21_0.b__3(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.<>c__DisplayClass16_0.b__1(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.<>c__DisplayClass155_0.b__0(QueryBase req)
at FakeXrmEasy.XrmFakedContext.<>c__DisplayClass155_0.b__2(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)

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?

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

[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

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....

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

@jordimontana82 I think I found the bug.

if (attributeInfo == null && attributeName.EndsWith("name"))
{
// Special case for referencing the name of a EntityReference
attributeName = attributeName.Substring(0, attributeName.Length - 4);
attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, attributeName);
if (attributeInfo.PropertyType != typeof(EntityReference))
{
// Don't mess up if other attributes follow this naming pattern
attributeInfo = null;
}
}
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

@jordimontana82 I think I found the bug.

if (attributeInfo == null && attributeName.EndsWith("name"))
{
// Special case for referencing the name of a EntityReference
attributeName = attributeName.Substring(0, attributeName.Length - 4);
attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, attributeName);
if (attributeInfo.PropertyType != typeof(EntityReference))
{
// Don't mess up if other attributes follow this naming pattern
attributeInfo = null;
}
}

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}'...>

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

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...

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

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

This property was autogenerated for multi-select optionset value

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?

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

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

Hey, I'm also using EarlyBoundGenerator