Dapper returns null for SingleOrDefault(DATEDIFF(...)) - asp.net-mvc-3

I have a SQL statement similar to:
SELECT DATEDIFF(Day, startDate, endDate) FROM Data WHERE ProjectId=#id
In the case where Data doesn't have any records for ProjectId, SQL Server returns null.
In Dapper, I execute this via:
value = conn.Query<int>("...").SingleOrDefault()
In this case, I expect the semantics of SingleOrDefault to mean "if this is null, return zero." In fact, my code is even more zero-friendly:
int toReturn = 0;
using (var conn = ...) {
toReturn = conn.Query<int>("SELECT DATEDIFF(...))");
}
return toReturn;
When I debug and step into this code, I find that the line yield return (T)func(reader) is throwing a null pointer exception.
Am I doing something wrong here, or is this by design?
(FYI, the work-around is to wrap my select in an ISNULL(..., 0))

In the case where Data doesn't have any records for ProjectId, SQL Server returns null.
In the case where Data doesn't have any matching records, SQL server does not really return null - it returns no rows. This scenario works fine:
var result = connection.Query<int>( // case with rows
"select DATEDIFF(day, GETUTCDATE(), #date)", new { date = DateTime.UtcNow.AddDays(20) })
.SingleOrDefault();
result.IsEqualTo(20);
result = connection.Query<int>( // case without rows
"select DATEDIFF(day, GETUTCDATE(), #date) where 1 = 0", new { date = DateTime.UtcNow.AddDays(20) })
.SingleOrDefault();
result.IsEqualTo(0); // zero rows; default of int over zero rows is zero
both of which work fine. The fact that you say ISNULL "fixes" it means that you are talking about a different scenario - the "I returned rows" scenario. Since that is the case, what your code is saying is "take this 1-or-more integers which contains a null, and map it as a non-nullable int" - that isn't possible, and the mapper is correct to throw an exception. Instead, what you want is:
int? result = connection.Query<int?>(...).SingleOrDefault();
Now, if there are rows, it is mapping the value to int?, and then applying the single-or-default. It you want that as an int, then maybe:
int result = connection.Query<int?>(...).SingleOrDefault() ?? 0;
If you need to be able to tell the difference between "zero rows" and "null result", then I would suggest:
class NameMe {
public int? Value {get;set;}
}
var row = connection.Query<NameMe>("select ... as [Value] ...", ...)
.SingleOrDefault();
if(row == null) {
// no rows
} else if(row.Value == null) {
// one row, null value
} else {
// one row, non-null value
}
or something similar

Related

Getting a column by string name

I'm trying to update a record given the customer Id, the row Id, and a dynamic column name.
Thus far I have the following, with the trouble spot marked by ***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
I haven't been able to find a good example of how to do this.
Addition after comments at the end
The reason you couldn't find examples is because it is not a good design.
Your method is very error prone, difficult to test and horrible to maintain. What if someone types the incorrect column name? What if you try to assign a string to the customer's birthday? And even if you would implement some string checking for column names and proposed values, then your program wouldn't work anymore after someone changes the names or the types of the columns.
So let's redesign!
Apparently you have a Customer with an Id and a property Rvals. This property Rvals also has a property Id.
You also have a function GetRValId that can convert a string rval to an int rvalId.
What you want, is given an Id and a string rval, you want to update one of the columns of the first Customer with this Idand rValId.
Side questions: Can there be more than one Customer with Id? In that case: are you sure Id is an ID? What do you want if there are more matching Customers? Update all customers or update only the first one? Which customer do you define as the first customer?
Leaving the side questions aside. We want a function signature that reports errors at compile time if you use non-existing customer properties, or if you try to assign a string to a Birthday. Something like this perhaps?
Update the name of the customer:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
Update the Birthday of the customer:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
This way you can't use any column that does not exist, and you can't assign a string to a DateTime.
You want to change two values in one call? Go ahead:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
Convinced? Let's write the function:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
Simple comme bonjour!
Addition after comments
So what does this function do? As long as you don't call it, it does nothing. But when you call it, it fetches a customer, performs the Action on the Customer you provided in the call, and finally calls SaveChanges.
It doesn't do this with every Customer, no it does this only with the Customer with Id equal to the provided Id and customer.RVals.Id == ... (are you still certain there is more than one customer with this Id? If there is only one, why check for RVals.Id?)
So the caller not only has to provide the Id, and the RVal, which define the Customer to update, but he also has to define what must be done with this customer.
This definition takes the form of:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
Well if you want, you can use other identifiers than customer, but it means the same:
x => {x.Name = X; x.BirthDay = Y;}
Because you put it on the place of the Action parameter in the call to UpdateCustomerRecord, I know that x is of type Customer.
The Acton statement means: given a customer that must be updated, what must we do with the customer? You can read it as if it was a Function:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
In the end it will do something like:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
So in the third parameter, called Action you can type anything you want, even call functions that have nothing to do with Customers (probably not wise). You have an input parameter of which you are certain that it is a Customer.
See my earlier examples of calling UpdateCustomerRecord, one final example:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
So the Action parameter is just a simpler way to say: "once you've got the Customer that must be updated, please perform this function with the Customer
So if you only want to update a given property of the customer write something like:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
Of course this only works if you know which property must be updated. But since you wrote "I am trying to update a specific cell." I assume you know which property the cells in this column represent.
It is not possible to pass the column name as the string value in LINQ. Alternate way to do it, if you have the limited number of the column name which can be passed then it can be achieved as below:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
Here, suppose you have the 5 columns that can be passed while calling the funcion UpdateRecord then you can add the 5 clauses in the WHERE as above.
Other way to do it dynamic LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);

How can I make my Dynamic Linq query more efficient

I've written an mvc controller function to handle dynamic queries, it's working great but with some more complicated queries it's really bogging down as much as 15 second response time. I used ObjectQuery.ToTraceString to get the sql and execute it against my database and I'm getting 1-2 second response times, not great but hugely disparate from the time it takes for my server to respond to the same query.
Here is my code:
public class QueryController : Controller
{
//
// GET: /Query/
Entities1 Context = new Entities1();
public JsonResult Query(
string select,
string table = "Product",
string where = "",
string groupBy = ""
)
IQueryable dataTable;
if (table == "Customer") dataTable = Context.Customers;
else if (table == "Product") dataTable = Context.Products;
else if (table == "Purchase") dataTable = Context.Purchase;
else dataTable = Context.Farms;
if (select == null) return null;
string whereClause = where;
string selectClaus = select;
string groupByClaus = groupBy;
IQueryable queryResults = dataTable;
if (where != "") queryResults = queryResults.Where(whereClause);
if (groupByClaus != "") queryResults = queryResults.GroupBy(groupByClaus, "it");
queryResults = queryResults.Select(selectClaus);
JsonResult result = new JsonResult();
result.Data = queryResults;
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
}
}
This code is supposed to handle requests like this:
?table=Purchase
&select=new (Customer.First as Name, Product.Id as ProdId)
&where=Category == 5
A query like the above takes about 700 ms, but if I try something more complicated it slows to a crawl (15 seconds):
?table=Purchase
&select=new (
count(true) as Total,
Key.Product as Product,
Key.CustomerRef as CustomerRef,
count(Price > 475) as BigPurchases,
count(PaidFor == false) as UnPaid,
count((Category != null) and (Comments == null) and Returns == null) as NoFeedback)
&groupBy=new (
Product.ProductName as Product,
CustomerRef as CustomerRef
)
In particular the navigation Property seems to be a problem, removing it speeds up the query considerably (3 sec):
?table=Purchase
&select=new (
count(true) as Total,
Key.Product as Product,
Key.CustomerRef as CustomerRef,
count(Price > 475) as BigPurchases,
count(PaidFor == false) as UnPaid,
count((Category != null) and (Comments == null) and Returns == null) as NoFeedback)
&groupBy=new (
ProductRef as Product,
CustomerRef as CustomerRef
)
The time all seems to get used iterating over the IEnumerable, in the code I provide I pass the data out and let the whatever underlying MVC code do whatever converting it wants, which takes approximately the time mentioned. However if I iterate over it myself, or use the ToList function I get similarly slow response times.
Does anyone have any ideas as to what is causing the long pause getting these entities?
Update
I added indexing to my database which sped things up, but it is still taking 20-30 times as long to execute the query in linq than it does in sql.

how to check for a value in the last 10 entries using linq to entities

I have method where I need to retrieve using EF the last ten entries in the database and check to see if there is a match between the value and the current term. Here is what I have thus far
public static int ValidatePassword(string username, string password, int securityUserId)
{
int validResult = 0;
/*Need to pass to client a value based upon success or failure of validation
* 0 - success
* 1 - password has already been used in the last 10 entries
* 2 - password does not meet CJIS requirements
*/
IEnumerable<string> oldpassword = null;
// Create a Regular Expression to determine whether or not special characters are present.
Regex regularExpression = new Regex("[^a-z0-9]");
//if id exists pull last ten passwords
if (securityUserId > 0)
{
long id = Convert.ToInt64(securityUserId);
using (var context = new SecurityEntities(string.Empty))
{
try
{
oldpassword = (from p in context.SecurityAudits
where p.SecurityUserId == id &&
p.OldPassword == password
orderby p.ActionDate descending
select p.OldPassword.Take(10).ToString()).ToList();
}
catch (Exception ex)
{
string err = string.Format("ValidateCJISPassword() was unable to return an object msg:{0}", ex.Message);
throw new Exception(err, ex.InnerException);
}
finally
{
context.Dispose();
}
}
}
else if (oldpassword == null)
{
//no matching record found now check other requirements
if ((password.Length >= DEFAULT_CJIS_PASSWORD_MIN_LENGTH) && regularExpression.IsMatch(password) && (password != username))
{
//success
validResult = 0;
}
else
{
//password does not meet standard CJIS requirements
validResult = 2;
}
}
else
{
//matching record was found
validResult = 1;
}
return validResult;
}
}
Where I am currently hung up is the query throws an exception on the ToString() method
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
I'm still learning EF and how linq works so I'm not sure what the best approach here is. Should I try to set the result to something other than IEnumerable like an array or List or is there another approach I should consider?
Thanks in advance,
Cheers,
Change this
oldpassword = (from p in context.SecurityAudits
where p.SecurityUserId == id &&
p.OldPassword == password
orderby p.ActionDate descending
select p.OldPassword.Take(10).ToString()).ToList();
To this
oldpassword = (from p in context.SecurityAudits
where p.SecurityUserId == id &&
p.OldPassword == password
orderby p.ActionDate descending
select p.OldPassword).Take(10).ToList();
The problem was that your Take(10) clause was not part of the whole result but inside the actual linq statement.. it goes on the outside of it to take the top 10 of the entire resultset.. then you do the ToList() which turns the whole thing into an array
The next problem is that you just created an array and assigned it to oldpassword
I don't see anything here that does anything with the array...
You need to do something like:
declare your array of strings
assign the array to the return of the linq query
evaluate the return for > 0 results
if > 0 then the password has been used in the last 10
if = 0 then new password should be ok, correct?
Now that I have an understanding of what I needed in the query I was able to also update the linq statement as follows:
var lastTenPassword = (from p in context.SecurityAudits.Take(10)
orderby p.ActionDate descending
where p.SecurityUserId == id
select p.OldPassword).ToList();
string oldpassword = lastTenPassword.Where(a => a == password).FirstOrDefault();
Testing is further down the line but now by moving the .Take() method inside the query I am explicitly grabbing the top ten where as my first attempt would have retrieved all the records and then grabbed the top ten.
For testing you can also see where I broke out the initial where() to first grab all records by id and then perform a filter on that set by looking for a matching password within that set.
Thanks again for your help

Row number in LINQ

I have a linq query like this:
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = ?
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
I want to populate recordIndex with the value of current row number in collection returned by the LINQ. How can I get row number ?
Row number is not supported in linq-to-entities. You must first retrieve records from database without row number and then add row number by linq-to-objects. Something like:
var accounts =
(from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new
{
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
})
.AsEnumerable() // Moving to linq-to-objects
.Select((r, i) => new AccountReport
{
RecordIndex = i,
CreditRegistryId = r.CreditRegistryId,
AccountNumber = r.AccountNo,
});
LINQ to objects has this builtin for any enumerator:
http://weblogs.asp.net/fmarguerie/archive/2008/11/10/using-the-select-linq-query-operator-with-indexes.aspx
Edit: Although IQueryable supports it too (here and here) it has been mentioned that this does unfortunately not work for LINQ to SQL/Entities.
new []{"aap", "noot", "mies"}
.Select( (element, index) => new { element, index });
Will result in:
{ { element = aap, index = 0 },
{ element = noot, index = 1 },
{ element = mies, index = 2 } }
There are other LINQ Extension methods (like .Where) with the extra index parameter overload
Try using let like this:
int[] ints = new[] { 1, 2, 3, 4, 5 };
int counter = 0;
var result = from i in ints
where i % 2 == 0
let number = ++counter
select new { I = i, Number = number };
foreach (var r in result)
{
Console.WriteLine(r.Number + ": " + r.I);
}
I cannot test it with actual LINQ to SQL or Entity Framework right now. Note that the above code will retain the value of the counter between multiple executions of the query.
If this is not supported with your specific provider you can always foreach (thus forcing the execution of the query) and assign the number manually in code.
Because the query inside the question filters by a single id, I think the answers given wont help out. Ofcourse you can do it all in memory client side, but depending how large the dataset is, and whether network is involved, this could be an issue.
If you need a SQL ROW_NUMBER [..] OVER [..] equivalent, the only way I know is to create a view in your SQL server and query against that.
This Tested and Works:
Amend your code as follows:
int counter = 0;
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = counter++
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
Hope this helps.. Though its late:)

How to handle no results in LINQ?

in this example code
public Company GetCompanyById(Decimal company_id)
{
IQueryable<Company> cmps = from c in db.Companies
where c.active == true &&
c.company_id == company_id
select c;
return cmps.First();
}
How should I handle if there is no data in cmps?
cmps will never be null, so how can I check for non existing data in a LINQ Query?
so I can avoid this
'cmps.ToList()' threw an exception of type ... {System.NullReferenceException}
when transforming it into, for example, a List
GetCompanyById(1).ToList();
Do I always need to wrap it up in a try catch block?
You can use Queryable.Any() (or Enumerable.Any()) to see if there is a member in cmps. This would let you do explicit checking, and handle it however you wish.
If your goal is to just return null if there are no matches, just use FirstOrDefault instead of First in your return statement:
return cmps.FirstOrDefault();
What about applying .Any or .Count() ?
Here's an example on MSDN
List<int> numbers = new List<int> { 1, 2 };
bool hasElements = numbers.Any();
Console.WriteLine("The list {0} empty.",
hasElements ? "is not" : "is");
Or just use the ?: operator
return myExample.Any() ? myExample.First() : null;
This will return the first one if there is one, or null if there isn't:
return (from c in db.Companies
where c.active == true &&
c.company_id == company_id
select c).FirstOrDefault();
Try return cmps.Count()==0?null:cmp.First()
That way if it is null it will simply return a null Company and if its not then it will return the first one in the list.
Check out http://en.wikipedia.org/wiki/Ternary_operation
var context = new AdventureWorksLT2008Entities();
var cust = context.Customers.Where(c => c.CustomerID == 1);
if (cust.Any())
{
Customer c = cust.First();
}

Resources