- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Fetch object reference exception w/inherited class
Joined: 05-Jul-2008
Lgen version: 2.6 final (v2.0.50727 runtime)
.NET 3.5, Adapter, General2008, Oracle 9i
I have the below function:
private void FetchCoders3(IDatabaseAdapter adapter)
{
LinqMetaData metaData = new LinqMetaData(adapter);
var q = (from rc in metaData.ReviewersAndCoders
join rcbc in metaData.RevCodeBillingCenters on rc.RevCodeId equals rcbc.RevCodeId
where rc.RevCodeFlag == "C"
orderby rc.Name
select new CodersLookup()
{
Name = rc.Name,
RevCodeId = rc.RevCodeId,
RevCodeFlag = rc.RevCodeFlag,
BillingCenterId = rcbc.BillingCenterId
}).Distinct();
List<CodersLookup> list = q.ToList();
Debug.WriteLine(list.Count);
}
(IDatabaseAdapter inherits from IDataAccessAdapter). The CodersLookup class looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CRMA.Model.EntityClasses;
namespace CRMA.Model.EntityLookups
{
public class CodersLookup : ReviewersAndCodersEntity
{
private long _billingCenterId;
public CodersLookup()
: base()
{
}
public long BillingCenterId
{
get { return _billingCenterId; }
set { _billingCenterId = value; }
}
public string RevCodeName
{
get { return "CODERS"; }
}
}
}
Basically the ReviewersAndCodersEntity has everything I need minus the one BillingCenterId field that comes from the related RevCodeBillingCentersEntity. So I decided to just create a new class inheriting from the main entity w/the new class having the additional field(s) I need. When I execute the query I get the below exception:
System.NullReferenceException occurred
Message="Object reference not set to an instance of an object."
Source="SD.LLBLGen.Pro.LinqSupportClasses.NET35"
StackTrace:
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.CreateResultsContainer(Type containerElementType, IElementCreatorCore generatedCodeElementCreator)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2. SetupProjectionElementsForExecution(QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2. ExecuteValueListProjection(QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase. System.Linq.IQueryProvider.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.Execute()
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.System. Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Collections.Generic.List1..ctor(IEnumerable
1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at CRMA.DataAccess.EntitySpecific.Server.LookupDataAdapter.FetchCoders3(IDatabaseAdapter adapter) in C:\Source\CRMA\Dev.Lgen\DAL\CRMA.DataAccess\EntitySpecific\Server\LookupDataAdapter.cs:line 111
InnerException:
If I change the CodersLookup class so it does not inherit from the source generated entity class (and duplicate the properties I was using in the base), the exception goes away. So inheritance appears to be the problem but I am not sure if this is something I can do, and what I might be missing if so?
I had a variation of the query working before with a prefetch path but that effected my distinct operation and I really need the data flat in this case for databinding in the same list. I suppose I could create a view in the database instead but would prefer to avoid that in cases. Assuming I can also use designer to just map in a field or two I need in one entity that happens to reside in another table... my trial just expired (doh) so that's not workable for a few days at least. Any good rules of thumb to assist in choosing between options of combining related data across entities / tables...?
- The LINQ trace dump is below:
-
Initial expression to process: value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2
1[ CRMA.Model.EntityClasses.ReviewersAndCodersEntity]).Join(value( SD.LLBLGen.Pro.LinqSupportClasses.DataSource2
1[ CRMA.Model.EntityClasses.RevCodeBillingCentersEntity]), rc => rc.RevCodeId, rcbc => rcbc.RevCodeId, (rc, rcbc) => new <>f__AnonymousType0`2(rc = rc, rcbc = rcbc)).Where(<>h__TransparentIdentifier1 => (<>h__TransparentIdentifier1.rc.RevCodeFlag = "C")).OrderBy(<>h__TransparentIdentifier1 => <>h__TransparentIdentifier1.rc.Name).Select(<>h__TransparentIdentifier1 => new CodersLookup() {Name = <>h__TransparentIdentifier1.rc.Name, RevCodeId = <>h__TransparentIdentifier1.rc.RevCodeId, RevCodeFlag = <>h__TransparentIdentifier1.rc.RevCodeFlag, BillingCenterId = <>h__TransparentIdentifier1.rcbc.BillingCenterId}).Distinct()
You posted the .NET runtime version. Please see the Guidelines thread in this forum for how to obtain the runtime lib version number. In any case: try the latest build from the customer area / latest demo and see if the problem is still there.
Svirid wrote:
Have the same problem today ... Pls Help .
Thanks a lot.
For you too: please answer my question above. We can't help you unless you give us detailed information. See my previous post.
Joined: 20-Oct-2008
LLBLGen Pro 2.6
SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll 2.6.08.0624 SD.LLBLGen.Pro.DQE.MySql.NET20.dll 2.6.08.0612 SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll 2.6.08.0616 SD.LLBLGen.Pro.TypeConverters.dll 2.6.0.0
We are using template MySQLSpecific.Net20 Version of MySQL 4.1
CoreLab.Data 4.70.23.0 CoreLab.MySql 4.70.31.0
The same situation as you noticed above ... tnx
SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll 2.6.08.0624 SD.LLBLGen.Pro.DQE.MySql.NET20.dll 2.6.08.0612 SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll 2.6.08.0616
A little bit old, and many things have been fixed since then. Would you please try using the latest available release?
Joined: 05-Jul-2008
Sorry for the delay but I am still receiving the original error with the code in the initial thread after trying the updated version. Any ideas?
SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll : 2.6.8.1013 SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll : 2.6.8.1001 SD.LLBLGen.Pro.DQE.Oracle10g.NET20.dll : 2.6.8.1009
.NET 3.5, Adapter, General2008, Oracle 9i
Exception:
System.NullReferenceException occurred
Message="Object reference not set to an instance of an object."
Source="SD.LLBLGen.Pro.LinqSupportClasses.NET35"
StackTrace:
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.CreateResultsContainer(
Type containerElementType, IElementCreatorCore generatedCodeElementCreator)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2. SetupProjectionElementsForExecution(
QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(
QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(
Expression handledExpression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider
.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.Execute()
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.SD.LLBLGen.Pro.LinqSupportClasses
.ILLBLGenProQuery.Execute()
at CRMA.DataAccess.EntitySpecific.Server.LookupDataAdapter.FetchReviewersCoders(
IDatabaseAdapter adapter, String revCodeType)
in C:\Source\CRMA\Dev.Lgen\DAL\CRMA.DataAccess\EntitySpecific\LookupDataAdapter.cs:line 102
InnerException:
Output:
- Method Enter: DataAccessAdapterBase.ExecuteMultiRowRetrievalQuery
- Method Enter: DataAccessAdapterBase.OpenConnection
- Method Exit: DataAccessAdapterBase.OpenConnection
- Method Exit: DataAccessAdapterBase.ExecuteMultiRowRetrievalQuery
- Method Exit: DataAccessAdapterBase.FetchEntityCollectionInternal(7)
- Method Exit: DataAccessAdapterBase.FetchEntityCollection(
- 'CRMA.Console.vshost.exe' (Managed): Loaded 'C:\Source\CRMA\Dev.Lgen\CRMA.Console\bin\Debug\SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll'
- Initial expression to process:
value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2
1[CRMA.Model. EntityClasses.ReviewersAndCodersEntity]).Join(value(SD.LLBLGen.Pro .LinqSupportClasses.DataSource2
1[CRMA.Model.EntityClasses .RevCodeBillingCentersEntity]), rc => rc.RevCodeId, rcbc => rcbc.RevCodeId, (rc, rcbc) => new <>f__AnonymousType0`2(rc = rc, rcbc = rcbc)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.rc.RevCodeFlag = value(CRMA.DataAccess.EntitySpecific.Server.LookupDataAdapter+<>c__DisplayClass1).revCodeType)).OrderBy(<>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.rc.Name).Select(<>h__TransparentIdentifier0 => new ReviewersAndCodersLookup() {Name = <>h__TransparentIdentifier0.rc.Name, RevCodeId = <>h__TransparentIdentifier0.rc.RevCodeId, RevCodeFlag = <>h__TransparentIdentifier0.rc.RevCodeFlag, BillingCenterId = <>h__TransparentIdentifier0.rcbc.BillingCenterId}).Distinct()
Method with exception:
private EntityCollection<ReviewersAndCodersLookup> FetchReviewersCoders(IDatabaseAdapter adapter,
string revCodeType)
{
EntityCollection<ReviewersAndCodersLookup> collection = null;
LinqMetaData metaData = new LinqMetaData(adapter);
ILLBLGenProQuery q = (ILLBLGenProQuery)
(from rc in metaData.ReviewersAndCoders
join rcbc in metaData.RevCodeBillingCenters on rc.RevCodeId equals rcbc.RevCodeId
where rc.RevCodeFlag == revCodeType
orderby rc.Name
select new ReviewersAndCodersLookup()
{
Name = rc.Name, RevCodeId = rc.RevCodeId, RevCodeFlag = rc.RevCodeFlag,
BillingCenterId = rcbc.BillingCenterId
}).Distinct();
collection = q.Execute() as EntityCollection<ReviewersAndCodersLookup>;
return collection;
}
The lookup class inheriting from the entity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CRMA.Model.EntityClasses;
namespace CRMA.Model.HelperClasses
{
public class ReviewersAndCodersLookup : ReviewersAndCodersEntity
{
private long _billingCenterId;
public long BillingCenterId
{
get { return _billingCenterId; }
set { _billingCenterId = value; }
}
}
}
DDL for the two tables is attached along with full source for a couple of the key files involved...
Filename | File size | Added on | Approval |
---|---|---|---|
RevCode.zip | 14,005 | 06-Nov-2008 23:14.03 | Approved |
Reproduced.
Looking into it.
(edit) The cause is that there's no factory for the ReviewersAndCodersLookup type. Defining an EntityCollection<ReviewersAndCodersLookup > is therefore not possible, there's no factory.
When I fix the issue with the type instantiation, I get a crash with the conversion of EntityCollection<ReviewersAndCodersEntity> to IEnumerable<ReviewersAndCodersLookup> which can't be done, according to the CLR, as it created an EntityCollection<ReviewersAndCodersEntity> because the factory obtained from the ReviewersAndCodersLookup instance dummy created from the ReviewersAndCodersLookup type produces a factory for ReviewersAndCodersEntity.
You're either adviced to do: 1) create a partial class of ReviewersAndCodersEntity and add the property you added to ReviewersAndCodersLookup to that partial class. OR 2) override in ReviewersAndCodersLookup the method: CreateEntityFactory() and in that override return an instance of a derived class of ReviewersAndCodersEntityFactory, which produces ReviewersAndCodersLookup instances instead of ReviewersAndCodersEntity instances.
Example of the latter:
Helper classes:
public class OrderDerived : OrderEntity
{
public string CompanyName { get; set; }
protected override IEntityFactory2 CreateEntityFactory()
{
return new OrderDerivedFactory();
}
}
public class OrderDerivedFactory : OrderEntityFactory
{
public override IEntityCollection2 CreateEntityCollection()
{
return new EntityCollection<OrderDerived>(this);
}
public override IEntity2 Create()
{
return new OrderDerived();
}
}
Query:
[Test]
public void GetEntitySetUsingProjectionOnDirectEntityType()
{
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData metaData = new LinqMetaData(adapter);
var q = from o in metaData.Order
join c in metaData.Customer on o.CustomerId equals c.CustomerId
where o.EmployeeId > 4
orderby o.OrderDate ascending
select new OrderDerived()
{
OrderId = o.OrderId,
CustomerId = o.CustomerId,
CompanyName = c.CompanyName,
EmployeeId = o.EmployeeId
};
int count = 0;
foreach(var v in q)
{
Assert.IsTrue(v.EmployeeId > 4);
Assert.IsFalse(string.IsNullOrEmpty(v.CompanyName));
count++;
}
Assert.AreEqual(334, count);
}
}
Personally, I'd go for the partial classes, as that doesn't create parallel hierarchies.
(edit) Please use the attached dll for testing it out.
Joined: 05-Jul-2008
I still get the same exception after making the changes for the factory, and the output looks the same as well.
Derived entity class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CRMA.Model.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
using CRMA.Model.FactoryClasses;
namespace CRMA.Model.EntityClasses
{
public class ReviewersAndCodersLookup : ReviewersAndCodersEntity
{
private long _billingCenterId;
public long BillingCenterId
{
get { return _billingCenterId; }
set { _billingCenterId = value; }
}
protected override IEntityFactory2 CreateEntityFactory()
{
return new ReviewersAndCodersLookupFactory();
}
}
}
The factory class:
public partial class ReviewersAndCodersEntityFactory : EntityFactoryBase2
{
public override IEntity2 GetNullEntitySurrogate()
{
var e = new ReviewersAndCodersEntity(-1);
e.Name = " ";
return e;
}
}
public class ReviewersAndCodersLookupFactory : ReviewersAndCodersEntityFactory
{
public override IEntityCollection2 CreateEntityCollection()
{
return new EntityCollection<ReviewersAndCodersLookup>(this);
}
public override IEntity2 Create()
{
return new ReviewersAndCodersLookup();
}
}
I prefer to go with the factory method as I do not want to "pollute" the original entity with various properties and code that only apply in specific instances. Although I was not aware there was this dependency on the factory class. In the absence of a factory, could it not just new it off using default constructor?
Let me know if there is anything I am missing as far as correcting the code to work. If needed I can do another sample app but the DB will be a problem...
Joined: 05-Jul-2008
Ahh, sorry. The hotfix you provided did remove the exception. However a problem remains in that the BillingCenterId field (from the joined entity) in the ReviewersAndCodersLookup entity appears to be 0 for all records. When I take the query from the output and run it in TOAD, each record in the results has a BillingCenterId value set.
thnk2wn wrote:
Ahh, sorry. The hotfix you provided did remove the exception. However a problem remains in that the BillingCenterId field (from the joined entity) in the ReviewersAndCodersLookup entity appears to be 0 for all records. When I take the query from the output and run it in TOAD, each record in the results has a BillingCenterId value set.
That's not reproducable with my code (as the CompanyName field is set in my returned data in my unit test).
If you fetch the query in an anonymous type (so refactor the code a bit so you don't fetch it into an EntityCollection), is the field set then? And when you fetch the data without the join? (so straight from the entity)
Btw, the ReviewersAndCodersEntityFactory class... is that also hand-made?
Joined: 05-Jul-2008
Yeah the problem appears to be with the execute statement getting the results into an EntityCollection which you are not doing in your sample. If I change the code to the below the BillingCenterId is set:
private EntityCollection<ReviewersAndCodersLookup> FetchReviewersCoders2(IDatabaseAdapter adapter,
string revCodeType)
{
EntityCollection<ReviewersAndCodersLookup> collection = null;
LinqMetaData metaData = new LinqMetaData(adapter);
var q = from rc in metaData.ReviewersAndCoders
join rcbc in metaData.RevCodeBillingCenters on rc.RevCodeId equals rcbc.RevCodeId
where rc.RevCodeFlag == revCodeType
orderby rc.Name
select new ReviewersAndCodersLookup()
{
Name = rc.Name,
RevCodeId = rc.RevCodeId,
RevCodeFlag = rc.RevCodeFlag,
BillingCenterId = rcbc.BillingCenterId
};
foreach (var e in q.Distinct())
{
Debug.WriteLine(string.Format("Name = {0}, ", e.Name));
Debug.WriteLine(string.Format("BillingCenterId = {0}", e.BillingCenterId));
}
return collection;
}
I can workaround for now by walking through and building the entity collection myself but obviously would prefer the execute straight into the entity collection which I would like to work with.
The ReviewersAndCodersEntityFactory class is not hand-made. It is the normal generated code; I just partialed it out just to add a custom method.
Joined: 05-Jul-2008
Ahh, problem solved. The Execute method requires that I serialize and deserialize the custom entity data (BillingCenterId in this case) in the derived entity for the properties that are not a part of the Fields collection. Once I added the below to ReviewersAndCodersLookup it works fine:
protected override void SerializeOwnedData(SerializationWriter writer, object context)
{
base.SerializeOwnedData(writer, context);
writer.WriteOptimized(_billingCenterId);
}
protected override void DeserializeOwnedData(SerializationReader reader, object context)
{
base.DeserializeOwnedData(reader, context);
_billingCenterId = reader.ReadOptimizedInt64();
}
2 questions left:
1) Is there a reason the entity factory is required for derived and it could not just use default ctors in their absence?
2) Any idea what release the hotfix might go into?
Thanks for the help
thnk2wn wrote:
Ahh, problem solved. The Execute method requires that I serialize and deserialize the custom entity data (BillingCenterId in this case) in the derived entity for the properties that are not a part of the Fields collection. Once I added the below to ReviewersAndCodersLookup it works fine:
protected override void SerializeOwnedData(SerializationWriter writer, object context) { base.SerializeOwnedData(writer, context); writer.WriteOptimized(_billingCenterId); } protected override void DeserializeOwnedData(SerializationReader reader, object context) { base.DeserializeOwnedData(reader, context); _billingCenterId = reader.ReadOptimizedInt64(); }
I already thought it would be something outside the scope of the Execute method, as your initial code shouldn't fail with the error you reported in your previous message.
2 questions left:
1) Is there a reason the entity factory is required for derived and it could not just use default ctors in their absence?
You need the factory, it's required for creating new instances of the derived type when the data is being fetched.
2) Any idea what release the hotfix might go into? Thanks for the help
In the next public build of v2.6. I guess that will be released this week. You can continue using the build I attached, that's the latest build of the code.