Hi Jeff,
We're using iAnywhere.Data.SqlAnywhere.v4.0 version 12.0.1.31524. Unfortunately we haven't been able to tell which instance is set to null. The reader itself still returns an SADatareader, the object is added to the list successfully after the recursion - it's just when it goes back to the reader after the 20th entry is added, it throws the NullReference Exception followed a TargetInvocationException.
Here is the full stack trace:
BlueK.Data.DebtMaster.dll!BlueK.Data.DebtMaster.DebtMasterDataContext.GetByCriteria<BlueK.Entities.AccountDetail>(BlueK.Data.QueryModel.Query query, int pageIndex, int pageSize, bool baseOnly, string bagType) Line 332 C#
[External Code]
BlueK.Data.dll!BlueK.Data.DataMapperHelper.GetCriteriaResult(string methodName, BlueK.Data.QueryModel.Query query, object parentObject, string manyToOneObjectType, string bagType) Line 294 C#
BlueK.Data.dll!BlueK.Data.DataMapperHelper.InitializeBags(object parentObject, string key, System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement> xml, System.Xml.Linq.XDocument parentXml, ref object parent) Line 120 C#
BlueK.Data.DebtMaster.dll!BlueK.Data.DebtMaster.DebtMasterDataContext.GetByCriteria<BlueK.Entities.Account>(BlueK.Data.QueryModel.Query query, int pageIndex, int pageSize, bool baseOnly, string bagType) Line 323 C# BlueK.Data.DebtMaster.dll!BlueK.Data.DebtMaster.DebtMasterDataContext.GetByCriteria<BlueK.Entities.Account>(BlueK.Data.QueryModel.Query query, int pageIndex, int pageSize) Line 175 C# BlueK.Data.DebtMaster.dll!BlueK.Data.DebtMaster.DebtMasterDataContext.GetByCriteria<BlueK.Entities.Account>(BlueK.Data.QueryModel.Query query) Line 162 C#
[External Code] BlueK.Data.DebtMaster.dll!BlueK.Data.DebtMaster.DebtMasterDataContext.GetById<BlueK.Entities.Account>(object key) Line 387 C# BlueKPayments.OCI.Business.dll!BlueKPayments.OCI.Business.AccountDataProvider.GetById(string accountId) Line 47 C# BlueKPayments.Services.Concrete.dll!BlueKPayments.Services.Concrete.AccountService.LoadAccount(string accountId) Line 115 C# BlueKPayments.Presentation.dll!BlueKPayments.Presentation.AccountEntryPresenter.CheckAccount(string accountId, string auth, string form3Auth, string ip, bool javascriptEnabled) Line 55 C#
BlueKPayments.UI.Controls.dll!BlueKPayments.UI.Controls.Login.ButtonSubmitClick(object sender, System.EventArgs e) Line 348 C#
[External Code]
And here is the recursive part of it, it occurs at the InitalizeManyToOne:
public static void InitializeManyToOne(object parentObject, IEnumerable<XElement> xml, XDocument parentXml, ref object parent)
{
foreach (var e in xml)
{
var xAttribute = e.Attribute("name");
if (xAttribute == null) continue;
var propteryName = xAttribute.Value;
var classAttr = e.Attribute("class");
if (classAttr == null) continue;
var manyToOneClass = classAttr.Value;
var parentKeyAttr = e.Attribute("parent-key-prop");
if (parentKeyAttr == null) continue;
var parentKeyProperty = parentKeyAttr.Value;
var keyNameAttr = e.Attribute("key-name");
if (keyNameAttr == null) continue;
var objectKey = keyNameAttr.Value;
var keyType = "";
var keyTypeAttr = e.Attribute("key-type");
if (keyTypeAttr != null)
keyType = keyTypeAttr.Value;
var keyDelineator = '|';
var keyDelineatorAttr = e.Attribute("key-delineator");
if (keyDelineatorAttr != null)
keyDelineator = keyDelineatorAttr.Value.ToCharArray()[0];
var key = parent.GetType().GetProperty(parentKeyProperty).GetValue(parent, null).ToString();
// extract the class name
var manyToOne = manyToOneClass.Split(',');
var className = manyToOne[0];
var lastIndex = className.LastIndexOf(".", StringComparison.Ordinal);
className = className.Substring(lastIndex + 1);// oneToManyClass.Substring(0, pos) + num.ToString();
// get the mapping for for the many-to-one name
var mappingXml = GetMapppingXml(parentObject, className);
var manyToOneMapping = mappingXml.Element("bluek-mapping");
if (manyToOneMapping == null) continue;
var assemblyAttr = manyToOneMapping.Attribute("assembly");
if (assemblyAttr == null) continue;
var namespaceAttr = manyToOneMapping.Attribute("namespace");
if (namespaceAttr == null) continue;
var manyToOneObjectType = GetObjectType(assemblyAttr.Value, namespaceAttr.Value);
// assign the bag values to the parent object
var mapping = parentXml.Element("bluek-mapping");
if (mapping == null) continue;
var parentNamespaceAttr = mapping.Attribute("namespace");
if (parentNamespaceAttr == null) continue;
var parentAssemblyAttr = mapping.Attribute("assembly");
if (parentAssemblyAttr == null) continue;
var parentObjectString = GetObjectType(parentAssemblyAttr.Value, parentNamespaceAttr.Value);
var parentObjectType = Type.GetType(parentObjectString);
if (parentObjectType == null) continue;
var propertyInfo = parentObjectType.GetProperty(propteryName);
if (keyType == "mv")
{
var result = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments()[0]));
var keys = key.Split(keyDelineator);
foreach (var singleKey in keys)
{
if (singleKey == "") continue;
var query = new Query();
query.Criteria.Add(new Criterion(objectKey, CriteriaOperator.Equal, singleKey));
result.Add(((IList)GetCriteriaResult("GetByCriteria", query, parentObject, manyToOneObjectType))[0]);
}
propertyInfo.SetValue(parent, result, null);
}
else
{
object result = null;
var query = new Query();
query.Criteria.Add(new Criterion(objectKey, CriteriaOperator.Equal, key));
result = GetBaseByCriteraResult("GetBaseByCriteria", query, parentObject, manyToOneObjectType);
var list = (IList)result;
if (list != null && list.Count != 0)
propertyInfo.SetValue(parent, list[0], null);
}
}
}
public static object GetBaseByCriteraResult(string methodName, Query query, object parentObject, string manyToOneObjectType, string bagType = "")
{
var mtoTrueType = Type.GetType(manyToOneObjectType);
var paramaterTypes = new[]
{
query.GetType(),
};
var methodInfo = parentObject.GetType().GetMethod(methodName, paramaterTypes);
if (methodInfo == null)
return null;
var genericArguments = new[] { mtoTrueType };
var parametersArray = new object[]
{
query
};
// this bit goes to the GetByCriteria below
var genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
return genericMethodInfo.Invoke(parentObject, parametersArray);
}
//the original code I posted is apart of this
public IList<T> GetByCriteria<T>(Query query, int pageIndex, int pageSize, bool baseOnly, string bagType) where T : class, new()
{
try
{
if (_connection.State == System.Data.ConnectionState.Closed)
_connection.Open();
}
catch (System.Exception ex)
{
Logger.Instance().LogException(ex);
}
var cKey = "";
var cOperator = "";
// lame basic query translator that will work for now
foreach (var criterion in query.Criteria)
{
if (criterion.Operator == CriteriaOperator.Equal || criterion.Operator == CriteriaOperator.Like)
{
cKey = criterion.Value.ToString();
cOperator = " = ";
}
}
var baseListType = typeof(List<>);
var listType = baseListType.MakeGenericType(typeof(T));
var newList = Activator.CreateInstance(listType) as IList<T>;
// initialize main object
var type = typeof(T);
var xml = DataMapperHelper.GetMapppingXml(this,type.Name);
var mapping = xml.Element("bluek-mapping");
if (mapping != null)
{
var xAttribute = mapping.Attribute("assembly");
if (xAttribute != null)
{
var attribute = mapping.Attribute("namespace");
if (attribute != null)
{
var objectType = DataMapperHelper.GetObjectType(xAttribute.Value, attribute.Value);
// get the main table info
var fileName = xml.Descendants("class").First().Attribute("table").Value;
var tableJoinName = "";
var joinColumn = "";
// if there is a join statement use it to build the query
if (xml.Descendants("join").Count() > 0)
{
tableJoinName = xml.Descendants("join").First().Attribute("table").Value;
joinColumn = xml.Descendants("join").First().Attribute("column").Value;
}
var join = from x in xml.Descendants("join").Descendants("id")
select new
{
column = x.Attribute("table").Value,
name = x.Attribute("name").Value,
type = x.Attribute("type").Value,
};
// get the main table info
string tableName = xml.Descendants("class").First().Attribute("table").Value;
var q = from x in xml.Descendants("id").Where(node => node.Parent.Name != "join" && node.Parent.Name != "bag" && node.Parent.Name != "many-to-one")
select new
{
column = x.Attribute("column").Value,
name = x.Attribute("name").Value,
type = x.Attribute("type").Value,
};
// build query
var sqlQuery = new StringBuilder();
sqlQuery.Append("select ");
foreach (var i in q)
{
sqlQuery.Append(tableName + "." + i.column + ",");
}
foreach (var i in @join)
{
sqlQuery.Append(tableJoinName + "." + i.column + ",");
}
sqlQuery.Remove(sqlQuery.Length - 1, 1); // remove the last comma
var from = new StringBuilder();
if (tableJoinName != "")
{
@from.Append(" from " + tableName + "," + tableJoinName);
@from.Append(" where " + tableName + "." + q.First().column + cOperator + tableJoinName + "." + joinColumn);
@from.Append(" and " + tableName + "." + q.First().column + cOperator + " ?");
}
else
@from.Append(" from " + tableName + " where " + tableName + "." + q.First().column + " = ?");
sqlQuery.Append(@from);
// execute query
try
{
var command = new SACommand(sqlQuery.ToString(), _connection);
command.Parameters.AddWithValue(q.First().column, cKey);
var reader = command.ExecuteReader();
var x = 0;
while (reader.Read())
{
x++;
var trueType = Type.GetType(objectType);
if (trueType == null) continue;
var obj = Activator.CreateInstance(trueType);
var j = 0;
foreach (var i in q)
{
var propertyInfo = trueType.GetProperty(i.name);
AssignPropertyValue(obj, i.type, reader, j++, propertyInfo);
}
// extract the join columns if they exsist
foreach (var i in @join)
{
var propertyInfo = trueType.GetProperty(i.name);
AssignPropertyValue(obj, i.type, reader, j++, propertyInfo);
}
//if (!baseOnly && trueType.Name == "Account" || !baseOnly && x <= 1)
if (!baseOnly)
{
DataMapperHelper.InitializeBags(this, cKey, xml.Descendants("bag"), xml, ref obj);
DataMapperHelper.InitializeManyToOne(this, xml.Descendants("many-to-one"), xml, ref obj);
}
//if(trueType.Name != "AccountDetail" && x <= 18)
if(trueType.Name != "Client" && (T)obj != null)
if (newList != null) newList.Add((T)obj);
}
}
catch (Exception ex)
{
throw ex;
}
}
}
}
_connection.Close();
return newList;
}
If there is any other information I could provide you with, please let me know!