cancel
Showing results for 
Search instead for 
Did you mean: 

.Net SqlAnywhere Reader Failure

Former Member
0 Kudos

We've recently run into some issues using the SqlAnywhere reader for our .Net application. We have a recursive reader which fetches several different accounts. We've noticed that whenever the number of accounts reaches exactly 20, the reader suddenly fails at the reader.Read() throwing the following error:

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

If we remove the recursive aspect, the reader is capable of continuing on without failing. Below is our while code. If you need any additional information, please don't hesitate to ask Thank you!

                    try

                        {

                            var command = new SACommand(sqlQuery.ToString(), _connection);

                            command.Parameters.AddWithValue(q.First().column, cKey);

                           

                            var reader = command.ExecuteReader();

                            var x = 0;

                            // this is where it fails after the 20th entry is added to the newList

                            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);

                                }

                                // This is the recursive bit that causes the while statement to run again.  

                                if (!baseOnly)

                                {

                                    DataMapperHelper.InitializeBags(this, cKey, xml.Descendants("bag"), xml, ref obj);

                                    DataMapperHelper.InitializeManyToOne(this, xml.Descendants("many-to-one"), xml, ref obj);

                                }

                                if (newList != null) newList.Add((T)obj);

                            }

                        }

                        catch (Exception ex)

                        {

                            throw ex;

                        }

Accepted Solutions (0)

Answers (1)

Answers (1)

jeff_albion
Employee
Employee
0 Kudos

Hi Jessica,

It's not immediately clear from your description how the SQL Anywhere .NET provider is encountering this situation - have you figured out which instance is set to null (is it the actual reader object?). What does the full stack trace look like?

What is the version and build number of the ADO.NET SQL Anywhere provider assembly?

I'm also not clear on how exactly the recursion happens - is this function defined within 'DataMapperHelper'?

Regards,

Jeff Albion
SAP Active Global Support

Former Member
0 Kudos

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!

jeff_albion
Employee
Employee
0 Kudos

Hi Jessica,

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.


Hmm - it would be good to know if you can narrow it down more precisely so that we could experiment more with the provider and match what you're trying. How many rows are really being returned back to the reader - is it exactly 20, or is it more/less?

Is "SADataReader.Read()" really on the top level of the exception stack? Are there any .InnerExceptions attached to the exception?

It could be a variety of problems but we'd likely need to be able to put a debugger on the statement to help you figure out what's possibly going wrong. Can you open an incident with SAP Support and upload the project to us?

Regards,

Jeff Albion

SAP Active Global Support