- Home
- LLBLGen Pro
- Bugs & Issues
Null Object Reference when saving an entity using WCF
Joined: 05-Oct-2005
I'm using:
- LLBLGen Pro version: 2.5 Final (September 24th, 2007) Runtime library version: 2.5.7.820 Adapter template Group .NET 3.0 Oracle 10g ODP.NET 10g v2 No hierarchies No custom templates
I have some simple code that creates and saves an entity that works when I call it normally. But when I call the code using WCF I get an "Object reference not set to an instance of an object." error.
StackTrace:
at System.Text.StringBuilder.Append(String value)
at SD.LLBLGen.Pro.DQE.Oracle.DynamicQueryEngine.CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse, Dictionary`2& fieldToParameter)
at SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateInsertDQ(IEntity2 entityToSave, IFieldPersistenceInfo[] persistenceInfoObjects)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue(List`1 queueToPersist, Boolean insertActions, Int32& totalAmountSaved)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntity(IEntity2 entityToSave, Boolean refetchAfterSave, IPredicateExpression updateRestriction, Boolean recurse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntity(IEntity2 entityToSave)
at WCFServiceLibrary2.MathService.PerformTransaction(Int32 clientNumber, Int32 IDKey) in C:\Projects.NET 2005\Test\WCF Transactions\WCFServiceLibrary2\Contracts.cs:line 334
I've tried the 'simple serialize/deserialize test' recommended in another thread and it had no errors.
string xml;
test.WriteXml(out xml);
TestTransaction1Entity deserialized = new TestTransaction1Entity();
deserialized.ReadXml(xml);
string deserializedXml;
deserialized.WriteXml(out deserializedXml);
if (!Debug.Equals(xml, deserializedXml))
{
Debug.WriteLine("xml not equal");
}
As you can see from the attached error the source shows as mscorlib and that it's StringBuilder that's throwing the error.
Here's the code that throws the error:
adapter = new DataAccessAdapter(_CONNSTRING, true);
testRecord = new TestTransaction1Entity();
testRecord.Id = IDKey.ToString();
testRecord.Value = String.Format("{0:000000} - Test Value", IDKey);
adapter.SaveEntity(testRecord);
IDKey has the value of '1'.
Any suggestions on how I can debug this to find out what's wrong?
Joined: 05-Oct-2005
This is the portion of the client code that calls the server:
BasicHttpBinding binding = new BasicHttpBinding();
binding.SendTimeout = new TimeSpan(10, 0, 0);
IWCF DBChannel = new ChannelFactory<IWCF>
(binding,
new EndpointAddress("http://localhost:8081/DBService")
).CreateChannel();
bool isSuccess = DBChannel.PerformTransaction(1, 2);
This is the server code:
public bool PerformTransaction(int clientNumber, int IDKey)
{
bool isSuccess = false;
DataAccessAdapter adapter = null;
TestTransaction1Entity testRecord;
try
{
adapter = new DataAccessAdapter(_CONNSTRING, true);
testRecord = new TestTransaction1Entity();
testRecord.Id = IDKey.ToString();
testRecord.Value = String.Format("{0:000000} - Test Value", IDClient);
adapter.SaveEntity(testRecord);
isSuccess = true;
}
catch (Exception e)
{
string error = "Error: " + Environment.NewLine +
"Message: " + e.Message + Environment.NewLine +
"Source: " + e.Source + Environment.NewLine +
"Stack: " + e.StackTrace + Environment.NewLine;
Log(error);
}
finally
{
adapter.Dispose();
}
return isSuccess;
}
The error occurs on the "adapter.SaveEntity(testRecord);" line.
Joined: 05-Oct-2005
I downloaded your code and compiled it in my solution and have tracked the error to the following lines of code in DynamicQueryEngine.cs:
protected override IActionQuery CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo,
IDbConnection connectionToUse, ref Dictionary<IEntityFieldCore, IDataParameter> fieldToParameter )
{
....
// add parameter
newParameter = (OracleParameter)base.Creator.CreateParameter(field, persistenceInfo, ParameterDirection.Input);
....
parametersText.Append(newParameter.ParameterName);
The problem is that CreateParameter() returns as a null for each of the fields and appending this null value to ParametersText gives me the error. This same code works just fine when I call SaveEntity() outside of WCF.
Any suggestions would be greatly appreciated.
Joined: 05-Oct-2005
In the absence of any responses, I've continued to investigate this issue.
I recompiled my code using LLBL runtime version 2.0.7.513 and the above (unchanged) code successfully inserts a record into the database with no errors.
Again, I didn't change any code, just recompiled the solution using the older version of LLBL.
Any chance this bug in v2.5 can be addressed?
Thanks,
Wm
William wrote:
I downloaded your code and compiled it in my solution and have tracked the error to the following lines of code in DynamicQueryEngine.cs:
protected override IActionQuery CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse, ref Dictionary<IEntityFieldCore, IDataParameter> fieldToParameter ) { .... // add parameter newParameter = (OracleParameter)base.Creator.CreateParameter(field, persistenceInfo, ParameterDirection.Input); .... parametersText.Append(newParameter.ParameterName);
The problem is that CreateParameter() returns as a null for each of the fields and appending this null value to ParametersText gives me the error. This same code works just fine when I call SaveEntity() outside of WCF. Any suggestions would be greatly appreciated.
That method can't return null. The call goes into:
/// <summary>
/// Creates a valid Parameter based on the passed in IEntityFieldCore implementation and the passed in IFieldPersistenceInfo instance
/// </summary>
/// <param name="field">IEntityFieldCore instance used to base the parameter on.</param>
/// <param name="persistenceInfo">Persistence information to create the parameter.</param>
/// <param name="direction">The direction for the parameter</param>
/// <param name="valueToSet">Value to set the parameter to.</param>
/// <returns>Valid parameter for usage with the target database.</returns>
public override IDataParameter CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, ParameterDirection direction, object valueToSet)
{
object value=base.GetRealValue(valueToSet, persistenceInfo.TypeConverterToUse, persistenceInfo.ActualDotNetType);
if(value==null)
{
value=System.DBNull.Value;
}
if(value is System.Enum)
{
value=Convert.ToInt32(valueToSet);
}
OracleParameter toReturn = null;
if(field.DataType == null)
{
// artifical field without real persistence info (e.g. an entityfield instance which is solely an expression)
toReturn = new OracleParameter(CreateParameterName(field.Alias), value);
}
else
{
toReturn = new OracleParameter();
toReturn.ParameterName = CreateParameterName(field.Alias);
toReturn.Direction = direction;
toReturn.IsNullable = persistenceInfo.SourceColumnIsNullable;
#if ODPNET
toReturn.Precision = persistenceInfo.SourceColumnPrecision;
toReturn.Scale = persistenceInfo.SourceColumnScale;
#endif
toReturn.Value = value;
#if MS
toReturn.OracleType = (OracleDbType)persistenceInfo.SourceColumnDbType;
#endif
#if ODPNET
if((persistenceInfo.SourceColumnDbType == (int)OracleDbType.Clob) ||
(persistenceInfo.SourceColumnDbType == (int)OracleDbType.Blob) ||
(persistenceInfo.SourceColumnDbType == (int)OracleDbType.BFile) ||
(persistenceInfo.SourceColumnDbType == (int)OracleDbType.NClob) ||
(persistenceInfo.SourceColumnDbType == (int)OracleDbType.XmlType))
{
toReturn.OracleDbType = (OracleDbType)persistenceInfo.SourceColumnDbType;
}
else
{
toReturn.DbType = SelectDbType((OracleDbType)persistenceInfo.SourceColumnDbType, persistenceInfo.SourceColumnPrecision, persistenceInfo.SourceColumnScale);
}
#endif
}
return toReturn;
}
which always returns an OracleParameter instance.
In the CreateInsertDQ routine in the OracleDQE, is fieldsPersistenceInfo filled with objects or does it contain nulls ? Are you sure the db specific assembly isn't out of sync with the dbgeneric assembly on the service?
Also, as you compiled against the sourcecode, could you step into CreateParameter and check where it returns exactly with null? This will help us greatly to check why it returns there and add a fix if necessary.
Joined: 05-Oct-2005
Sorry I wasn't specific. An OracleParameter instance is returned; however the 'Value' property is null for each of the fields.
In the CreateInsertDQ routine in the OracleDQE, is fieldsPersistenceInfo filled with objects or does it contain nulls ?
The only 'null' is in the 'TypeConverterToUse' property, all other information is filled in.
Are you sure the db specific assembly isn't out of sync with the dbgeneric assembly on the service?
Yes, I cleaned out the folder and regenerated both simultaneously with the Gen Pro v2.5.
Also, as you compiled against the sourcecode, could you step into CreateParameter and check where it returns exactly with null?
Like I said above, the null is in the 'Value' property. The line:
toReturn.Value = value;
Doesn't seem to work, 'value' contains '2' and yet 'toReturn.Value' contains a null AFTER the line executes. That is very odd, but please look at the attached screen shot to see the code as I stepped through it and the variables in the watch box at the bottom.
Also I discovered that this is generating a system error:
.NET Runtime version 2.0.50727.42 - Fatal Execution Engine Error (7A05E2B3) (80131506)
And the error occurs in 'toReturn' which is an Oracle.DataAccess.Client.OracleParameter client. And if I recompile your code with the older version of ODP 10.1 the error goes away.
This problem doesn't occur unless I use WCF, so this is an Oracle ODP v10.2 driver error?
William wrote:
Sorry I wasn't specific. An OracleParameter instance is returned; however the 'Value' property is null for each of the fields.
This means that the entity's values aren't deserialized properly. Could you check if the entity received at the service indeed has the fields set to values it had when it was send?
Also, even if the Value of a parameter is null, it doesn't crash. Could you please give me the exact location of the crash?
Also, as you compiled against the sourcecode, could you step into CreateParameter and check where it returns exactly with null?
Like I said above, the null is in the 'Value' property. The line:
toReturn.Value = value;
Doesn't seem to work, 'value' contains '2' and yet 'toReturn.Value' contains a null AFTER the line executes. That is very odd, but please look at the attached screen shot to see the code as I stepped through it and the variables in the watch box at the bottom.
![]()
You didn't attach a screenshot (only to the first post, which doesn't reveal a lot )
I can only imagine that toReturn.Value stays null if the type of the value doesn't match the type of the parameter, but then again, it's really strange.
Also I discovered that this is generating a system error:
.NET Runtime version 2.0.50727.42 - Fatal Execution Engine Error (7A05E2B3) (80131506)
And the error occurs in 'toReturn' which is an Oracle.DataAccess.Client.OracleParameter client. And if I recompile your code with the older version of ODP 10.1 the error goes away.
This problem doesn't occur unless I use WCF, so this is an Oracle ODP v10.2 driver error?
Not sure, as WCF has not a lot to do with oracle in this case.
I have a question: you do have a separate .exe for client and for service, right? In the past sometimes people started remoting applications with just 1 exe, which went wrong for wicked reasons.
Joined: 05-Oct-2005
This means that the entity's values aren't deserialized properly. Could you check if the entity received at the service indeed has the fields set to values it had when it was send?
Yes the entity (which is created on the service side, not received) has the fields set correctly.
Also, even if the Value of a parameter is null, it doesn't crash. Could you please give me the exact location of the crash?
As I stated in my 3rd post the crash occurs in the following location, the last line shown below is doing a stringbuilder append to parametersText and crashes because parameterName is null:
protected override IActionQuery CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo,
IDbConnection connectionToUse, ref Dictionary<IEntityFieldCore, IDataParameter> fieldToParameter )
{
....
// add parameter
newParameter = (OracleParameter)base.Creator.CreateParameter(field, persistenceInfo, ParameterDirection.Input);
....
parametersText.Append(newParameter.ParameterName);
I can only imagine that toReturn.Value stays null if the type of the value doesn't match the type of the parameter, but then again, it's really strange.
toReturn.Value is type object and value is type object {string} and as I said this value will get transferred successfully with no error if I use the older ODP v10.1
I have a question: you do have a separate .exe for client and for service, right?
Yes I'm using two different .exe files.
I'll try to add the screen shot successfully this time, sorry about that.
Also, from googling the error I mentioned above a possible solution might be to install VS2005 SP1 so I'll try that next.
William wrote:
This means that the entity's values aren't deserialized properly. Could you check if the entity received at the service indeed has the fields set to values it had when it was send?
Yes the entity (which is created on the service side, not received) has the fields set correctly.
Also, even if the Value of a parameter is null, it doesn't crash. Could you please give me the exact location of the crash?
As I stated in my 3rd post the crash occurs in the following location, the last line shown below is doing a stringbuilder append to parametersText and crashes because parameterName is null:
protected override IActionQuery CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse, ref Dictionary<IEntityFieldCore, IDataParameter> fieldToParameter ) { .... // add parameter newParameter = (OracleParameter)base.Creator.CreateParameter(field, persistenceInfo, ParameterDirection.Input); .... parametersText.Append(newParameter.ParameterName);
Parameter name is set using CreateParameterName, which always has to return a value... This is one heck of a weird error...
But I think it might be in the assembly referencing/redirection. Read on..
I can only imagine that toReturn.Value stays null if the type of the value doesn't match the type of the parameter, but then again, it's really strange.
toReturn.Value is type object and value is type object {string} and as I said this value will get transferred successfully with no error if I use the older ODP v10.1
Ok. The old 10.1 odp.net is compiled for .net 1.x. Using the 10.2 ODP.NET, you'll get 2 versions: one compiled for .net 1.x and one for 2.x. We've seen some very odd behavior when people redirected the 2.x version via assembly redirects to the OracleDQE build against 10.1. (the v2.5 native is build against 2.20... of ODP.NET 10.2, though we also ship a DQE build against 10.1).
What I find really strange is that the values which get passed into the routine are all valid, though the parameter objects become empty. I have no idea why this happens.
I have a question: you do have a separate .exe for client and for service, right?
Yes I'm using two different .exe files. I'll try to add the screen shot successfully this time, sorry about that.
![]()
Ah, turbo-pascal nostalgia
Also, from googling the error I mentioned above a possible solution might be to install VS2005 SP1 so I'll try that next.
Ok.
Other machines have the same error btw?
Joined: 05-Oct-2005
But I think it might be in the assembly referencing/redirection. Read on..
To rebut this assertion of yours I combed through my solution yet again and happened to find one redirection in the host project. I removed this and suddenly I'm getting values as I should. However it's the end of a long day and I want to try it again in the morning before I'm certain I'm not imagining things
Hopefully this solves the problem and if so, thanks so much!
Ah, turbo-pascal nostalgia Wink
Everyone here makes fun of my color scheme too, alas, am I the only programmer left who has good taste?!
William wrote:
But I think it might be in the assembly referencing/redirection. Read on..
To rebut this assertion of yours
I combed through my solution yet again and happened to find one redirection in the host project. I removed this and suddenly I'm getting values as I should. However it's the end of a long day and I want to try it again in the morning before I'm certain I'm not imagining things
![]()
Hopefully this solves the problem and if so, thanks so much!
I think it's the way it could go wrong: wacky assembly redirects. I'm not sure why Oracle's code isn't capable of working with these redirects, but anyway, with the native build against 2.x code of oracle, it shouldn't be necessary to use the redirects
Ah, turbo-pascal nostalgia Wink
Everyone here makes fun of my color scheme too, alas, am I the only programmer left who has good taste?!
![]()
haha, "good taste", you're funny