I have a SQL CLR aggregate function which sporadically throws a System.NullReferenceException exception when executed against the same dataset. The purpose of the custom aggregate is to:
Return latest(x, y) where x is a DATETIME column and y is an INTEGER column.
The value for column y for the most recent value of column x will be returned.
The dataset being hit by the query is a subset of 142,145 rows of a 2,931,563 row table, with the aggregation resulting in (when it runs) 141,654 rows being returned.
The code for the CLR aggregate function is as follows:
using System.Data.SqlTypes;
using System.Runtime.InteropServices;
using Microsoft.SqlServer.Server;
[StructLayout(LayoutKind.Sequential)]
[SqlUserDefinedAggregate(Format.Native, IsInvariantToDuplicates = true, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty = true, Name = "Latest")]
public class Latest
{
private SqlDateTime latestDateTime;
private SqlInt32 latestValue;
public void Init()
{
latestDateTime = SqlDateTime.Null;
latestValue = SqlInt32.Null;
}
public void Accumulate(SqlDateTime recordDateTime, SqlInt32 value)
{
if (latestDateTime.IsNull)
{
latestDateTime = recordDateTime;
latestValue = value;
}
else
{
if (recordDateTime > latestDateTime)
{
latestDateTime = recordDateTime;
latestValue = value;
}
}
}
public void Merge(Latest value)
{
if ((value.latestDateTime < latestDateTime) || (latestDateTime.IsNull))
{
latestValue = value.latestValue;
latestDateTime = value.latestDateTime;
}
}
public SqlInt32 Terminate()
{
return latestValue;
}
};
As far as I can tell there's nowhere in the function that can result in a null reference, assuming that SQL server is following the contract outlined on MSDN (though it's much more likely I'm wrong than SQL Server!). So in short, what am I missing here?
To clarify:
I believe I've met the requirements of the contract for a SqlUserDefinedAggregate (all required methods implemented)
The code initialises all member variables in the Init method (again part of the contract implementation) to ensure that if SQL re-uses (as it's permitted to) an instance of the aggregate for a different group it's cleaned down and non-null
Clearly I've missed a nuance of the contract that I'm expected to meet as I can see no reason for the NullReferenceException to be thrown. What have I missed?
This may turn out not to be the answer, but as I need to include code, I won't post it as a comment.
Having a NULL value for value.latestDateTime wouldn't cause this error. You only get the NULLReferenceException when an OBJECT is null, and you try to refer to it (by accessing it's properties for instance).
The only place in your code where I see you referencing an object is in the Merge void. The object is value which is of type Latest.
I know you say it's not theoretically possible for value to ever be null, but that can't be proven or disproven in the code you've posted since Merge is public and therefore accessed from other applications.
I have found in OOP that it's safest to ALWAYS null-check any object before referencing it. All you have to do is change your Merge code to this (if my c# memory serves...if my syntax is off, I'm sure you'll get the idea):
public void Merge(Latest value)
{
if (value != null)
{
if ((value.latestDateTime < latestDateTime) || (latestDateTime.IsNull))
{
latestValue = value.latestValue;
latestDateTime = value.latestDateTime;
}
}
}
It's up to you if you want to do something else when value IS null. But this makes this stretch of code absolutely safe from NullReferenceExceptions, rather than trusting to "theoretically shouldn't be possible", which almost always means "IS possible" ; )
Everything does appear to be correct. Except for possibly one thing. Can either of the source columns for the two input parameters be NULL? The code itself does seem to handle NULLs. However, if there are NULLs in the data then I think you have the IsInvariantToNulls property of the SqlUserDefinedAggregate attribute set incorrectly as it should be false since a NULL in either field could affect the result.
Also:
The datatype of the field being used for recordDateTime is DATETIME and not DATETIME2, correct?
Try running the query after adding OPTION(MAXDOP 1) as a query hint as that should prevent the Merge method from being called and that can help narrow down the issue. If the exception never happens with OPTION(MAXDOP 1) then it most likely has to do with the Merge method. But if it still occurs, then it most likely has nothing to do with the Merge method.
To clarify some questioning of how SqlDateTime handles comparison operators (i.e. the < and >) when .IsNull is true: it is handled properly and will return a false if either side of those operators has .IsNull == true.
I would also remove the [StructLayout(LayoutKind.Sequential)] decorator. The default .NET handling should be fine.
I would add explicit check for NULL:
public void Accumulate(SqlDateTime recordDateTime, SqlInt32 value)
{
if (latestDateTime.IsNull)
{
latestDateTime = recordDateTime;
latestValue = value;
}
else
{
if (!recordDateTime.IsNull)
{
if (recordDateTime > latestDateTime)
{
latestDateTime = recordDateTime;
latestValue = value;
}
}
}
}
Besides, your logic in Merge seems wrong... I'd repeat the same code as in Accumulate:
public void Merge(Latest value)
{
if (latestDateTime.IsNull)
{
latestDateTime = value.latestDateTime;
latestValue = value.latestValue;
}
else
{
if (!value.latestDateTime.IsNull)
{
if (value.latestDateTime > latestDateTime)
{
latestDateTime = value.latestDateTime;
latestValue = value.latestValue;
}
}
}
}
Related
I wrote a generic method to retrieve single values from database (MSSQL Server).I encountered into a case that I need to get a Boolean value from DB.
As you can see in the code below, a Object local field (IsExist) gets the result.
When the value in DB is False GenericScalar() method return False (as it should)
and the condition: if (IsExist == null) in GetWanLineDisconnectionData() is true and the return block is executing, even though IsExist is False and not null.
Why is that?
How can I overcome this problem?
private void GetWanLineDisconnectionData()
{
string q = "SELECT WanLineDiscconection FROM AdditionalProjectsData WHERE SpCall= " + "'" + spCall + "'";
object IsExist = Orange.ProjectManagment.DAL.Database.GenericScalar<object>(q);
if (IsExist == null) {
return;
}
if (bool.Parse(IsExist) == true) {
RadWanDiscYes.Checked = true;
} else {
RadWanDiscNo.Checked = true;
}
}
Database method:
public static T GenericScalar<T>(string query)
{
OleDbConnection connection = new OleDbConnection(sqlConnString);
connection.Open();
OleDbCommand cmd = new OleDbCommand(query, connection);
try
{
var result = cmd.ExecuteScalar();
if (result == null)
{
return default(T);
}
else
{
return (T)result;
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConnection(ref connection);
}
}
EDIT:
maybe a few screen shoots will better demonstrate it:
(note that: GetWanLineDisconnectionData() is written in VB.NET and GenericScalar() is written in C# on a different project in the solution):
in the beginning IsExist is nothing (null).
the query finds one row and the value of the bool column WanLineDiscconection is false and IsExist is set to false as it should be.
here is the problem, the program enters the if block and IsExist is not nothing (null).
The variable foo in object foo = false is definitely not null, so the premise in your title is incorrect.
ExecuteScalar() returns null when there are no rows. In that case, the method GenericScalar() return default(T), which for object will be null.
How to solve this depends on what your data looks like. You probably want to represent the result in a nullable int, or int? instead of object:
var exists = Orange.ProjectManagment.DAL.Database.GenericScalar<int?>(q);
RadWanDiscYes.Checked = exists.GetValueOrDefault() > 0;
See How does GetValueOrDefault work?, What is the default value of the nullable type "int?" (including question mark)?.
Aside from that: you generally also don't want to write handy wrappers around database queries, because you're reinventing the wheel poorly, opening up your application to SQL injection. That being said, there's a lot more going wrong in that method, including but not limited to not disposing your database connection and rethrowing exceptions without their stacktrace.
You´re mixing compile-time and runtime information. Whilst GenericScalar<object>(q) is an information that exists at compile-time the type returned from ExecuteScalar at compile-time is just object. You expect it to be of type boolean, which might or might not be true for your specific case. However this is not relevant to the compiler.
In short that means that T actually is object, not whatever you expect it to be from your database. And as CodeCaster allready mentioned the default-value for object is just null.
Si I´d suggest to use GenericScalar<bool> instead of GenericScalar<object> because you seem to know what your query actually returns - in your case a bool. Then default(T) evaluates to false, not to null.
Consider the following code snippet
public class Class1
{
public enum TestEnum
{
Value1 = 1,
Value2 = 2
}
public void TestCall()
{
/*some standard DB code returning an SqlDataReader...*/
SqlDataReader rdr = com.ExecuteReader();
Item item = new Item();
/*original code*/
/*Database "Type" is a varchar() field containing an integer, please dont ask why :)*/
if (rdr["Type"].GetType() == typeof(DBNull))
{
item.Type = TestEnum.Value1;
}
else if ((string)rdr["Type"] == "1")
{
item.Type = TestEnum.Value2;
}
else if ((string)rdr["Type"] == "2")
{
item.Type = TestEnum.Value1;
}
else
{
item.Type = TestEnum.Value1;
}
/*suggested code*/
item.Type = rdr["Type"] as TestEnum? ?? TestEnum.Value1; //<- default / null value to use
}
}
public class Item
{
public Class1.TestEnum Type;
}
During code review, a colleague of mine pointed out that i could replace the cascading IFs ("original code") with a single line ("suggested code")
While the suggested code runs just fine, I get a NullReferenceException when inspecting "rdr["Type"] as TestEnum?" at debug time.
I was wondering if this is a sign of underlying problems with the suggested code, what is the prefered way of mapping a database value to an enum, and what are your thoughts on this kind of code generally speaking.
The suggested code is just wrong - it will not throw exception, but will always evaluate to TestEnum.Value1.
Why? The reader returns the value as object. The as T? operator will evaluate to non null value only if the object represents a boxed T value. When the object contains string as in your case, or even if was a boxed int (the underlying type of your enum), still the operator as TestEnum? will evaluate to null, hence the expression will hit the ?? TestEnum.Value1 condition.
Shortly, don't rely on such tricks. If you want to improve that code, create a method (which can be reused from other places if needed):
static TestEnum ToTestEnum(object dbValue)
{
TestEnum value;
return Enum.TryParse(dbValue as string, out value) ? value : TestEnum.Value1;
}
and then change the original code like
item.Type = ToTestEnum(rdr["Type"]);
As far as I knew, Object.GetType() should never return null. (related discussion)
Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.
Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
I suspect that dynamic or private type is the cause of null, so I write my class library for test:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
In another project, get the dynamic type static field and call GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?
DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.
However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.
DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.
public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:
RuntimeBinderException: Cannot perform runtime binding on a null
reference
You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
Right now I am working on a simple program, and this is a problem I've been thinking over many times. Many times I run my methods twice because of checking on the return value before running them, and I would like to know if there is a way I can prevent this, like using the returned value from the method I am checking against. It's quite hard to explain so here is a real life example from my program.
public class SFDBRepository
{
public static Domain.SF.SFObject GetSFOrder(string WorkOrd)
{
//As you can see here i'm checking on the output of this method, before trying to return it.
if (Domain.SF.SF.GetOrder(WorkOrd) != null)
{
//If the value is not null (My method returns null if no result), return the object
return Domain.SF.SF.GetOrder(WorkOrd);
}
//Same thing happens here. My method runs twice every time almost.
else if(Domain.Building_DeliveryPerformance.Building_DeliveryPerformance.GetObject(WorkOrd) != null)
{
return Domain.Building_DeliveryPerformance.Building_DeliveryPerformance.GetObject(WorkOrd);
}
else
{
return null;
}
}
}
You can simplify this down to the following code, which will only call those methods once and make the code much more readable:
public class ShopFloorDBRepository
{
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string workOrd)
{
return Domain.ShopFloor.Shopfloor.GetOrder(workOrd) ??
Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(workOrd);
}
}
To explain why this works - the ?? operator (the null-coalescing operator!) basically says "if the returned value on the left hand side of the ?? is null, then return the value of the expression on the right hand side".
This way you only need to call your functions once.
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
//As you can see here i'm checking on the output of this method, before trying to return it.
Domain.ShopFloor.ShopFloorObject wo = Domain.ShopFloor.Shopfloor.GetOrder(WorkOrd);
if (wo != null)
{
//If the value is not null (My method returns null if no result), return the object
return wo;
}
//Same thing happens here. My method runs twice every time almost.
Domain.ShopFloor.ShopFloorObject yowo = Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(WorkOrd);
if(yowo != null)
{
return yowo;
}
/* default return */
return null;
}
PS
You're kinda doing the "Factory Pattern"
See
http://www.dofactory.com/net/factory-method-design-pattern
Looks to me like you could be using a temporary variable to hold the result, which you can test and return.
public class ShopFloorDBRepository
{
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
var result = Domain.ShopFloor.GetOrder(WorkOrd);
if (result != null) return result;
...
This is a common paradigm, especially when the method being called is expensive and/or has side effects you don't wish to incur twice.
Here, the "var" declaration sets the type of "result" to the type returned by the method being called; you could also use the name of the actual type.
If you wish to make two different kinds of tests like this, you'll need two different variables unless they have the same type (which in this case it appears they do).
An alternate mechanism that does require the full type, that you'll also see:
public static ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
ShopFloorObject result;
if ( (result = Domain.ShopFloor.GetOrder(WorkOrd)) != null )
return result;
if ( (result = Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(WorkOrd)) != null)
return result;
return null;
Here you're explicitly declaring the type of the return value, then making the two calls you've indicated, testing the results against null, and returning the first non-null value.
When my website gets to the following bit of code, it falls down with an exception as follows:
System.InvalidCastException: Object cannot be cast from DBNull to other types.
For the interests of brevity, I'm showing only the relevant code (it's a 4000+ LOC file I've been given).
if (dr["STAGE"] is DBNull)
{
dto.Stage = 1; // This is the line throwing the exception, according to stack trace
}
else
{
dto.Stage = Convert.ToInt32(dr["STAGE"]);
}
Here, dr is a DataRow object that is the result of a query to a database, dto is a basic class that just holds some properties, of which dto.Stage is an int member.
I've looked at other questions with the same error message, but most of them seem to suggest "Check if it's DBNull", which I'm already doing.
So can someone suggest a solution?
Use == instead of is
if (dr["STAGE"] == DBNull.Value)
{
}
Use this slightly more efficient approach
int stageOrdinal = dr.GetOrdinal("STAGE");
while (dr.Read()) {
dto = new DataTransferObject();
if (dr.IsDBNull(stageOrdinal)) {
dto.Stage = 1;
} else {
dto.Stage = dr.GetInt32(stageOrdinal);
}
//TODO: retrieve other columns.
dtoList.Add(dto);
}
Accessing the columns by their index is faster than accessing them by name. The index of a column can be retrieved with the GetOrdinal method of the DataReader. This is best done before the loop.
Use the System.Data.DataRow.IsNull function instead.
if(dr.IsNull("Stage"))
{
...
}
Below is an example of a nullable data type which you can use to avoid DBNull errors.
The example below is not a genuine solution for the issue you have, but is an example of how you can go about solving it. Think of it as learning to fish, instead of being given a fish.
I pulled this from http://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
class NullableExample
{
static void Main()
{
int? num = null;
if (num.HasValue == true)
{
System.Console.WriteLine("num = " + num.Value);
}
else
{
System.Console.WriteLine("num = Null");
}
// y is set to zero
int y = num.GetValueOrDefault();
// num.Value throws an InvalidOperationException if num.HasValue is false
try
{
y = num.Value;
}
catch (System.InvalidOperationException e)
{
System.Console.WriteLine(e.Message);
}
}
}
#MarcGravell had the right of it in his comment:
Ignore the exact line number in the stack-trace; the numbers can be
slightly off - I see that all the time. Run it in a debugger with
break-points instead, or just add extra logging while you nail it down
#Sergey's answer is just plain wrong.