Replace Linq to Entity value in Projection - linq

I want to relace a value retrieved from a L2E projection to an expanded string.
The table contains a column called Status which can have a value "0" or "1" and in my L2E I have
var trans = from t in db.Donation
select new DonationBO()
{
Status = t.Status
};
What I want is to return either of the strings "Pending" or "Committed" instead of "0" or "1".
How can I do this here?

If Status is a string you could simply do:
var trans = from t in db.Donation
select new DonationBO()
{
Status = t.Status == "0" ? "Pending" : "Committed"
};

Related

LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)'?

Im getting this error when I execute the following code, any Ideas how to fix it?
LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)' method, and this method cannot be translated into a store expression.
result = items.ToList()
.Select(b => new BatchToWorkOnModel()
{
BatchID = b.Batch.ID,
SummaryNotes = b.Batch.Notes,
RowVersion = b.Batch.RowVersion,
Items = items
.Select(i => new ItemToWorkOnModel()
{
SupplierTitle = i.Title,
ItemID = i.ID,
BatchID = i.BatchID ?? 0,
ItemDate = i.PubDate,
// KB - Issue 276 - Return the correct Outlet name for each item
Outlet = i.Items_SupplierFields != null ? i.Items_SupplierFields.SupplierMediaChannel != null ? i.Items_SupplierFields.SupplierMediaChannel.Name : null : null,
Status = ((short)ItemStatus.Complete == i.StatusID ? "Done" : "Not done"),
NumberInBatch = i.NumInBatch,
Text = string.IsNullOrEmpty(i.Body) ? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "..."),
IsRelevant = i.IsRelevant == 1,
PreviouslyCompleted = i.PreviouslyCompleted > 0 ? true : false
}).ToList()
})
.FirstOrDefault();
It seems Math.Min is not implemented by the EF query provider. You should be able to fix it by simply applying AsEnumerable on your items collection to do the expression using Linq to Objects instead;
Items = items.AsEnumerable().Select(i => new ItemToWorkOnModel()...
If you add a where condition to the item selection (seems a little strange to take all items in the whole table), you'll want to add it before AsEnumerable() to allow EF to do the filtering in the database.
Also, you only want the first result from the query, but you're fetching all of them using ToList() before cutting the list down to a single item. You may want to remove the ToList() so that EF/the underlying database can return only a single result;
result = items.Select(b => new BatchToWorkOnModel()...
You do not need Math.Min.
The line in question is:
Text = string.IsNullOrEmpty(i.Body)
? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "...")
So what does this line return?
If i.Body is null or empty it returns an empty string. If it is 50 or more characters long it returns a substring of 50 characters and appends "...".
If the length is less than 50 it takes a substring with the length of the string and appends an empty string. But that's just the original string.
Text = string.IsNullOrEmpty(i.Body)
? "" : (i.Body.Length < 50 ? i.Body : i.Body.Substring(0, 50) + "...")

Dapper returns null for SingleOrDefault(DATEDIFF(...))

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

LINQ: Using a ternary ( ?: ) with LINQ let is not enough, need an "IF" but can't seem to get it to work

I am trying to include an IF within my LET in LINQ but i can't get it to work, it seems to work for the ternary operator, but this is TRUE or FALSE and i need to have more than 2 options.
I think this explains it well
Basically i have a select which selects items using joins from a DB. Then the i get the status for each record but i have to make a join on separate tables depending on the type from products.type
var tst = from p in products join i in info on p.id equals i.pid
// if p.type = "home" then ...
let status = from s in homestatus
select new { status = s.status }
// if p.type ="offshore" then
let status = from s in offshorestatus
select new { status = s.status }
// if p.type ="internal" then
let status = from s in internalestatus
select new { status = s.status }
select new {
name = p.name,
status = status.StatusText
}
Anybody have any ideas how to do a standard IF so i can select which STATUS (let) i wish to execute.
Thanks in advance
You can do that with the conditional operator (1)
var tst = from p in products join i in info on p.id equals i.pid
let status = p.type = "home" ? homestatus.Select(s=>s.status) :
p.type = "offshore" ? offshorestatus.Select(s=>s.status) :
p.type = "internal" ? internalestatus.Select(s=>s.status) : null
select new {
name = p.name,
status = status != null ? status.StatusText : string.Empty;
}
If you are not using the status for anything else than the StatusText you could also do it like this
var tst = from p in products join i in info on p.id equals i.pid
let status = (p.type = "home" ? homestatus.Select(s=>s.status.StatusText) :
p.type = "offshore" ? offshorestatus.Select(s=>s.status.StatusText) :
p.type = "internal" ? internalestatus.Select(s=>s.status.StatusText) : null) ?? string.Empty
select new {
name = p.name,
status = status;
}
(1) A ternary operator is any operator that takes three arguments, of which there at present is only one in C# called the conditional operator

LINQ to retrieve first matching item, or a blank value for each item in a list

I am having some trouble with a linq query
var matches = from po in purchaseOrders
from poItem in po.Items
where TestMatch(poItem)
select new Item(poItem);
purchaseOrders is a List
Each PurchaseOrder contains a List
What I need for a result, is the first poItem that matches (based on the result of TestMatch(poItem)) in each purchase order, OR a blank Item object.
So that in the end matches.Count == purchaseOrders.Count
Currently, I only get items that match in a PO, and I'm not sure how to ensure I only get ONE item per PO. And I don't know how to ensure if there is no match, that I get a blank Item for that PO.
It sounds like you want something like:
var matches = from po in purchaseOrders
let poItem = po.Items.FirstOrDefault(item => TestMatch(item))
select new { PO = po,
Item = poItem == null ? null : new Item(poItem) };
With C# 4 you can use a method group conversion for the argument to FirstOrDefault:
var matches = from po in purchaseOrders
let poItem = po.Items.FirstOrDefault(TestMatch(item)
select new { PO = po,
Item = poItem == null ? null : new Item(poItem) };

linq subquery returning null

I have an odd linq subquery issue.
Given the following data structure:
Parents Children
------- --------
Id Id
ParentId
Location
HasFoo
(obviously this is not the real structure, but it's close enough for this example)
I'm able to run this query and get a desired result:
bool b = (from p in Parents
from c in Children
where p.Id == 1 && c.ParentId == p.Id && c.Location == "Home"
select c.HasFoo).SingleOrDefault();
So if there is a child that has the Location "Home" for a Parent of Id 1, I will get that Child's "HasFoo" value, otherwise, I'll get false, which is the "default" value for a bool.
However, if I try and write the query so I have a list of Parent objects, like so:
var parentList = from p in Parents
select new ParentObject
{
ParentId = p.ParentId,
HasHomeChildren = p.Children.Count(c => c.Location == "Home") > 0,
HasHomeChildrenWithFoo = (from c in p.Children where c.Location == "Home" select c.HasFoo).SingleOrDefault()
}
I get the following error when iterating over the list:
The null value cannot be assigned to a member with type System.Boolean which is a non-nullable value type.
I don't see where this "null" value is coming from, however.
I wonder if the compiler is inferring HasHomeChildrenWithFoo to be bool, but then actually casting to a nullable bool (thus messing up your SingleOrDefault call). At any rate, I'd be willing to bet you could fix it with a cast to a nullable type in that final select which you can then manually default to false when null. It'd probably make the error go away, but it's kind of a brute-force kludge.
var parentList = from p in Parents
select new ParentObject
{
ParentId = p.ParentId,
HasHomeChildren = p.Children.Any(c => c.Location == "Home"),
HasHomeChildrenWithFoo = (from c in p.Children where c.Location == "Home" select (bool?)c.HasFoo) ?? false)
}

Resources