Creating reusable chunks of LINQ to SQL - linq

I am trying to break up linq to sql queries to make them a bit more readable.
Say I want to return all orders for product which in the previous year had more than 100 orders. I have this query:
from o in _context.Orders
where (from o1 in _context.Orders
where o1.Year == o.Year - 1 && o1.Product == o.Product
select o1).Count() > 100
select o;
What I'd like to be able to do is to put the nested query in a reusable function:
private IQueryable<Order> LastSeasonOrders(Order order)
{
return (from o in _context.Orders
where o.Year == order.Year - 1 && o.Product == order.Product
select o);
}
which then lets me change the original query to:
from o in _context.Orders
where LastSeasonOrders(o).Count() > 100
select o;
This doesn't work however with an exception saying that the method call cannot be translated to SQL when the query is run.
Any quick tips on the correct way to achieve this?

What about something like -
void Main()
{
TypedDataContext _context = ...
var query =
(
from o in _context.Orders
where LastSeasonOrders(_context , o).Count() > 100
select o
);
...
}
public static Func<TypedDataContext, Order, IQueryable<Order>>
LastSeasonOrders = CompiledQuery.Compile
(( TypedDataContext _context, Order order) =>
from o in _context.Orders
where o.Year == order.Year - 1 && o.Product == order.Product
select o
);
?
It would be best to verify that the sql produced is the same as that produced by your original query.

I am just shooting from the hip but have you tried change return type of LastSeasonOrders to IQueryable<Order>?

Related

Performing a sum subquery using linq

Please could someone assist with the below query. I'm trying to select the Sum of all receipts per Client. But the 'Total =' sub query in my select section is not working and I'm getting the following error:
Unable to create a constant value of type 'AppName.Domain.Entities.AccountingEntry'. Only primitive types or enumeration types are supported in this context.
I've already tried doing a ToList() after that query but this results in the same problem. What is the standard way of selecting a Sum as a subquery using linq?
var receipts = (from ae in repo.AccountingEntries
join c in repo.Clients on ae.ClientId equals c.ClientId
join m in repo.Memberships on c.MembershipId equals m.MembershipId
where
(ae.EntryDate <= start) &&
(ae.ClientId != null) &&
(ae.AccountingEntryTypeId == (byte)Shared.AccountingEntryTypes.Receipt)
select new AppName.Reports.Clients.AgeAnalysis.Receipt
{
ClientId = (Guid)ae.ClientId,
Client = c.FirstName + " " + c.LastName,
Membership = c.Membership.Name,
Total = (from ae2 in repo.AccountingEntries where ae2.ClientId == ae.ClientId select ae2.Total).Sum()
});
Thanks,
Gary
This seems overly complex and unnecessary. If your entity model is set up properly, you can simplify by removing the joins and using a "group by" clause. Something along the lines of:
from ae in c.AccountingEntries
where
(ae.EntryDate <= start) &&
(ae.ClientId != null ) &&
(ae.AccountingEntryTypeId == (byte)Shared.AccountEntyrTypes.Receipt)
group by
ae.Client into g
select
new AppName.Reports.Clients.AgeAnalysis.Receipt
{
ClientId = (Guid)g.Key.ClientId,
Client = g.Key.FirstName + " " + g.Key.LastName,
Membership = g.Key.Membership.Name,
Total = g.Sum( p => p.Total )
}

In Operator in Linq

I tried to use the suggestion provided here for using In operator in linq but, i am not able to convert my requirement into LINQ statement.
Below is the SQL query which i need to convert to Linq
select *
from navigator_user_field_property
where user_id = 'albert'
and field_id in (
select field_id
from navigator_entity_field_master
where entity_id = 1
and use_type = 0)
order by field_id
I want this to be converted to a Efficient Linq.
Most of the answers deal with the predetermined list of string array which is not working in my case.
Thanks
Looks like a join to me:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert"
join field in db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
on navigator.FieldId equals field.FieldId
select navigator;
Note that this will return the same value multiple times if there are multiple fields with the same ID - but I suspect that's not the case.
You could do a more literal translation like this:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert" &&
db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
.select(f => f.FieldId)
.Contains(navigator.FieldId)
select navigator;
... and that may end up translating to the same SQL... but I'd personally go with the join.
Here is an efficient and readable LINQ query:
var fields =
from field in db.navigator_entity_field_masters
where field.entity_id == 1 && field.user_type == 0
select field;
var properties =
from property in db.navigator_user_field_properties
where property.user_id == "albert"
where fields.Contains(property.field)
select property;
Look mama!! Without joins ;-)

How to do a simple Count in Linq?

I wanted to do a paging style table, but NeerDinner example fetches the entire data into a PaggingList type, and I have more than 10 000 rows to be fetched, so I skipped that part.
so I come up with this query
var r = (from p in db.Prizes
join c in db.Calendars on p.calendar_id equals c.calendar_id
join ch in db.Challenges on c.calendar_id equals ch.calendar_id
join ca in db.ChallengeAnswers on ch.challenge_id equals ca.challenge_id
join cr in db.ChallengeResponses on ca.challenge_answer_id equals cr.challenge_answer_id
where
p.prize_id.Equals(prizeId)
&& ch.day >= p.from_day && ch.day <= p.to_day
&& ca.correct.Equals(true)
&& ch.day.Equals(day)
orderby cr.Subscribers.name
select new PossibleWinner()
{
Name = cr.Subscribers.name,
Email = cr.Subscribers.email,
SubscriberId = cr.subscriber_id,
ChallengeDay = ch.day,
Question = ch.question,
Answer = ca.answer
})
.Skip(size * page)
.Take(size);
Problem is, how can I get the total number of results before the Take part?
I was thinking of:
var t = (from p in db.JK_Prizes
join c in db.JK_Calendars on p.calendar_id equals c.calendar_id
join ch in db.JK_Challenges on c.calendar_id equals ch.calendar_id
join ca in db.JK_ChallengeAnswers on ch.challenge_id equals ca.challenge_id
join cr in db.JK_ChallengeResponses on ca.challenge_answer_id equals cr.challenge_answer_id
where
p.prize_id.Equals(prizeId)
&& ch.day >= p.from_day && ch.day <= p.to_day
&& ca.correct.Equals(true)
&& ch.day.Equals(day)
select cr.subscriber_id)
.Count();
but that will do the query all over again...
anyone has suggestions on how can I do this effectively ?
If you take a query as such:
var qry = (from x in y
select x).Count();
...LINQ to SQL will be clever enough to make this a SELECT COUNT query, which is potentially rather efficient (efficiency will depend more on the conditions in the query). Bottom line is that the count operation happens in the database, not in LINQ code.
Writing my old comments :Well i was facing the same issue some time back and then i came up with LINQ to SP =). Make an SP and drop that into your entities and use it.you can get write Sp according to your need like pulling total record column too. It is more easy and fast as compare to that whet you are using wright now.
You can put count for query logic as well as, see the sample as below:
public int GetTotalCountForAllEmployeesByReportsTo(int? reportsTo, string orderBy = default(string), int startRowIndex = default(int), int maximumRows = default(int))
{
//Validate Input
if (reportsTo.IsEmpty())
return GetTotalCountForAllEmployees(orderBy, startRowIndex, maximumRows);
return _DatabaseContext.Employees.Count(employee => reportsTo == null ? employee.ReportsTo == null : employee.ReportsTo == reportsTo);
}

what cast is missing in linq syntax?

tblUserRole Permission =
(oCurrenUserPermission.GetPermission(Convert.ToString(Session["Navigation"])));
if (Permission.IsInsert == 1)
{
}
public IQueryable GetPermission(string sPageName)
{
IQueryable query;
#region MyRegion
query = from r in this.Context.tblUserRoles
join p in this.Context.tblPageInfos on r.PageID equals p.PageID
where r.Record_Status == 2 && p.PageName == sPageName
select r;
return query;
#endregion
}
Above syntax show the bellow error:
Error 1 Cannot implicitly convert type 'System.Linq.IQueryable' to 'AkijBeverage.ServiceObject.tblUserRole'. An explicit conversion exists (are you missing a cast?) E:\Project-Akij\09-July-2010\AkijBeverage\AkijBeverage\SecurityUserControls\UCUserRole.ascx.cs 63 43 AkijBeverage
How to solve this ?
As far as I can tell, that query is just not compatible with tblUserRole.
It's going to return a IEnumereable<bool> instead of a tblUserRole.
The real problem is that you are selecting the IsInsert in two different spots: In the query, and again in the if()
The easiest way would be just return the full tblUserRole object:
return (from r in this.Context.tblUserRoles
join p in this.Context.tblPageInfos on r.PageID equals p.PageID
where r.Record_Status == 2 && p.PageName == sPageName
select r).SingleOrDefault();
Currently you're returning a query - not a single item. What do you want to return?
Here's one option, for example:
public tblUserRole GetPermission(string sPageName)
{
return (from r in this.Context.tblUserRoles
join p in this.Context.tblPageInfos on r.PageID equals p.PageID
where r.Record_Status == 2 && p.PageName == sPageName
select r).FirstOrDefault();
}
This will return the first matching role, or null if there aren't any.
If you do want to return a query, it would be better as a strongly typed IQueryable<T>:
public IQueryable<tblUserRole> GetPermissions(string sPageName)
{
return from r in this.Context.tblUserRoles
join p in this.Context.tblPageInfos on r.PageID equals p.PageID
where r.Record_Status == 2 && p.PageName == sPageName
select r;
}
Then you'd need to change your calling code to something like this:
tblUserRole Permission
oCurrenUserPermission.GetPermissions(Convert.ToString(Session["Navigation"]))
.FirstOrDefault();
The problem is you have an IQueryable and are attempting to assign to a "Type" without a cast.
Either change your method to:
public IQueryable<tblUserRole > GetPermission(string sPageName)
and then do (First/FirstOrDefault/Single/etc..)
oCurrenUserPermission.GetPermission(...).FirstOrDefault();
Or cast it and cross your fingers and select one.
(tblUserRole)oCurrenUserPermission.GetPermission(...).FirstOrDefault();
Side Note
I would suggest you work on naming your code in a more friendly maner. No need to prepend the "type" at the beginning. For example a possible better way to name everything:
string navigationValue = Convert.ToString(Session["Navigation"]);
UserRole permission =
currentUserPermission.GetPermission(navigationValue ).FirstOrDefault();
//If this isn't a boolean i would consider using one
if (permission.IsInsert == 1)
{
}
public IQueryable<UserRole> GetPermission(string sPageName)
{
#region MyRegion
var query = from r in this.Context.UserRoles
join p in this.Context.PageInformation on r.PageID equals p.PageID
where r.Record_Status == 2 && p.PageName == sPageName
select r;
return query;
#endregion
}

conditional assignments in Linq to SQL methods

How do I do something like the following, assinging new values based on condition within a linq method but keeping all results.
int x= 100;
var results= from r in Results where r.id==id select r
{ if r.value==x set r.result="Found"}
You're not really meant to - ideally queries shouldn't have side effects. I mean you can do:
var results = Results.Where(r => r.id == id)
.Select(r => {
if (r.value == x)
{
r.result = "Found";
}
return r;
};
I'd generally try not to though...
Note that this really won't work in LINQ to SQL etc.
It's probably better to make a second pass so you can keep your query clean and side-effect-free.
int x = 100;
List<Result> results = (from r in Results
where r.id == id
select r).ToList();
results.ForEach(r => r.result = r.value == x ? "Found" : "Not Found");
Linq should not be used in that way. I would iterate over the results of the query and then change them.
var results= from r in Results where r.id==id select r;
foreach (var r in results.ToList()) { if (r.value==x) r.result="Found"; }

Resources