LINQ OrderBy string parameter - linq

I'm sure this isn't the most efficient way, but I have a LINQ statement and want to OrderBy a field based off a switch case (this is in the OnSorting Event of a GridView)
switch (e.SortExpression)
{
case "Date":
field = "btu.Date";
break;
}
var ds (from btu in dc.BTUs
join zone in dc.BTUZones on btu.BTUZoneKey equals zone.BTUZoneKey
orderby field ascending
select new { btu.BTUKey, btu.Date, zone.BTUZoneName, btu.BTUValue }).ToList();
gv.DataSource = ds;
gv.DataBind();
my guess is I can't place a string literal in the query. Should I change my Sort Expression to match the field I want to OrderBy on or is there a way to place a parameter in the query and it accept it (I can't orderby date I need to orderby btu.Date)

try code:
you have directly could not used Linq Object in btu
So i have alter solution this problem.
var ds = (from btu in dc.BTUs
join zone in dc.BTUZones on btu.BTUZoneKey equals zone.BTUZoneKey
select new { btu.BTUKey, btu.Date, zone.BTUZoneName, btu.BTUValue }).ToList();
switch (e.SortExpression)
{
case : "Date":
ds= ds.OrderBy(c=>c.Date).ToList();
break;
case : "Name"
ds= ds.OrderBy(c=>c.BTUZoneName).ToList();
break;
}
and add Another Solution :
Using Linq System.Linq.Dynamic
Go to Vs studio->Tools->NewGet Package Manager->Package Manager Console
Enter Command
Install Install-Package System.Linq.Dynamic -Version 1.0.7
This Reference Add Your Project
After Using below Code:
using System.Linq.Dynamic;
string Field="";
switch (e.SortExpression)
{
case : "Date":
Field= "Date";
break;
case : "Name"
Field= "BTUZoneName";
break;
}
var ds = (from btu in dc.BTUs
join zone in dc.BTUZones on btu.BTUZoneKey equals zone.BTUZoneKey
select new { btu.BTUKey, btu.Date, zone.BTUZoneName, btu.BTUValue })
.OrderBy(Field).ToList();

Perhaps the easiest way to get LINQ to sort on arbitrary runtime data is to install the System.Linq.Dynamic NuGet package. (Here's a link to documentation about the library's dynamic operators.)
However, you'll have to change your LINQ from query syntax to method syntax:
using System.Linq.Dynamic;
...
var ds =
dc.BTUs
.Join(dc.BTUZones,
btu => btu.BTUZoneKey,
zone => zone.BTUZoneKey,
(btu, zone) => new { btu.BTUKey, btu.Date, zone.BTUZoneName, btu.BTUValue })
.OrderBy("BTUZoneName") // Or you can use a string variable
// or function call for the parameter.
.ToList();
Another way of building dynamic runtime LINQ queries is to create your own custom expression trees. (This is how the code in System.Linq.Dynamic is implemented.) This is a Microsoft article that explains the basics.

Related

String extension method in Linq query

How to use string extension method in linq query:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name.ToFriendlyUrl() ==
name.ToFriendlyUrl());
return newsType;
}
Above query x.Name.ToFriendlyUrl() is not allowed at the minute. Is anyone know how to achieve with it.
Extension methods are indeed allowed in LINQ queries, moreover the LINQ methods themselves are implemented as extension methods.
It's quite another issue however, to use extension methods (or most other methods) in LINQ-to-SQL or LINQ-to-Entities queries. Those queries are not actually run in the C# code, but they are treated like expressions that are translated to SQL. I.e.
db.News.Where(x => x.Published).Select(x => x.Name)
is translated to the SQL Statement
Select Name
From News
Where Published = 1
and it's results are returned to the C# code.
Since there is not way to transfer the ToFriendlyUrl() method to SQL, your code throws an error.
You have basically, two solutions/workarounds. One is to transform the call to a form could be translated into SQL, e.g. if the ToFriendlyUrl() method was just:
public static string ToFriendlyURL(this string value)
{
return value.ToLower();
}
you can inline that code in the LINQ call, and that would work. If however, the methods is more complex, than your only solution is to just fetch the data from the base and then process it on the C# side:
var newsTypeQuery = db.NewsTypes.Where(x => // other conditions, if any);
var newsTypes = newsTypes.ToList(); //forces execution of the query
// the result is now a C# list
var newsType = newsTypes.FirstOrDefault(x =>
x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
Assuming the NewsTypes is an IQueryable this is a result of Entity Framework not being able to convert you extension method into SQL (how should it?). Unless you can rewrite your predicate into something that Entity Framework can translate into SQL you will have to perform the query client side:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.AsEnumerable().FirstOrDefault(x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
return newsType;
}
Notice how AsEnumerable() has been added before FirstOrDefault. Unfortunately this may pull all the rows returned by NewsTypes from the server to client and thus may be quite costly.
This
var newsType = db.NewsTypes.FirstOrDefault(
x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
can't be done in Entity Framework. ToFriendlyUrl is an extension method. It's something that is in the "client" computer. The query will be executed on the SQL server. The SQL server doesn't have a ToFriendlyUrl function.
The "standard" solution is to save in a second column named FriendlyName a precalculated version of the ToFriendlyUrl(), so your query becomes:
var friendlyName = name.ToFriendlyUrl();
var newsType = db.NewsTypes.FirstOrDefault(
x => x.FriendlyName == friendlyName);
Instead try like this
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name == name).ToFriendlyUrl();
return newsType;
}

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

LINQ to Entities complex query

Is it possible ...??? I have 4 DropDownLists on my main page and the
user may select from any, all or some of
the DropDownLists. I am capturing their selection (or non-selection) using a SESSION
variable. What I would like to be able to do is pass the session
variable values to my Data Access Layer and build a WHERE clause
(maybe using StringBuilder) and then place that variable SOMEHOW into
my query expression. Is that possible??? Sorry, I'm a newbie. Thanks ~susan~
public class DLgetRestaurants
{
FVTCEntities db = new FVTCEntities();
public List<RESTAURANT> getRestaurants(string cuisineName, string priceName, string cityName)
[Build a string based on the values passed to the function]
{
var cuisineID = db.CUISINEs.First(s => s.CUISINE_NAME == cuisineName).CUISINE_ID;
List<RESTAURANT> result = (from RESTAURANT in db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price")
where **[USE STRINGBUIDER EXPRSSION HERE]**
select RESTAURANT).ToList();
return result;
}
}
You can compose Where conditions which are linked by a logical AND relatively easy in LINQ extension method syntax:
var query = db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price");
if (userHasSelectedInDDL1)
query = query.Where(r => r.PropertyForDDL1 == ValueFromDDL1);
if (userHasSelectedInDDL2)
query = query.Where(r => r.PropertyForDDL2 == ValueFromDDL2);
if (userHasSelectedInDDL3)
query = query.Where(r => r.PropertyForDDL3 == ValueFromDDL3);
if (userHasSelectedInDDL4)
query = query.Where(r => r.PropertyForDDL4 == ValueFromDDL4);
List<RESTAURANT> result = query.ToList();
For a much more flexible solution to build queries dynamically the Dynamic LINQ Library recommended by boca is probably the better choice.
I have done this in the past using the Dynamic Linq Library.

MvcContrib Grid Sorting

Am testing out MvcContrib's grid for sorting.
Am using LightSpeed as my ORM
Problem: getting compile error on: listOfRfidTags = ...
The type arguments for method 'System.Linq.Enumerable.OrderBy(System.Collections.Generic.IEnumerable, System.Func, System.Collections.Generic.IComparer)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
public ActionResult Index(GridSortOptions sort)
{
IEnumerable<RfidTag> listOfRfidTags = uow.RfidTags;
if(sort.Column != null) {
listOfRfidTags = listOfRfidTags.OrderBy(sort.Column, sort.Direction);
}
ViewData["sort"] = sort;
return View(listOfRfidTags);
}
view:
#Html.Grid(Model).Columns(column =>{
column.For(a => Html.ActionLink("Edit", "Edit", new { id = a.Id })).Named("Edit");
column.For(a => a.TagCode).Named("TagCode").Sortable(true);
column.For(a => a.Number);
})
You are getting this compiling error because you are trying to use an OrderBy extension method that is only defined in MvcContrib and not in System.Linq.
In order to fix it, you just need to use the following line:
using MvcContrib.Sorting;
And then you can use the OrderBy method as in your original code:
listOfRfidTags = listOfRfidTags.OrderBy(sort.Column, sort.Direction);
Although itowlson answer works, he just reimplements what the OrderBy extension method in MvcContrib already does (see SortExtensions.cs).
The OrderBy extension method takes a delegate for getting the sort key, not a column and direction. So this line:
listOfRfidTags = listOfRfidTags.OrderBy(sort.Column, sort.Direction);
needs to look something like this:
listOfRfidTags = listOfRfidTags.OrderBy(r => r.SomeProperty);
(or OrderByDescending depending on the sort.Direction). The trouble is that SomeProperty can't be determined at compile time because you want it to come from sort.Column. This means that if you want to use LINQ then you'll probably need to use Dynamic LINQ or Reflection to extract the property you want to sort on e.g.
PropertyInfo property = typeof(RfidTag).GetProperty(sort.Column);
listOfRfidTags = listOfRfidTags.OrderBy(r => property.GetValue(r));
However, since you are using LightSpeed as your ORM, you can bypass LINQ and use the core API, which does allow dynamic column names:
Order order = Order.By(sort.Column);
if (sort.Direction == SortDirection.Descending))
order = order.Descending();
IList<RfidTag> listOfRfidTags = uow.Find<RfidTag>(new Query { Order = order });
This has the side benefit that the sorting will happen on the database instead of in the Web application.

linq help - newbie

how come this work
public IQueryable<Category> getCategories(int postId)
{
subnusMVCRepository<Categories> categories = new subnusMVCRepository<Categories>();
subnusMVCRepository<Post_Category_Map> postCategoryMap = new subnusMVCRepository<Post_Category_Map>();
var query = from c in categories.GetAll()
join pcm in postCategoryMap.GetAll() on c.CategoryId equals pcm.CategoryId
where pcm.PostId == 1
select new Category
{
Name = c.Name,
CategoryId = c.CategoryId
};
return query;
}
but this does not
public IQueryable<Category> getCategories(int postId)
{
subnusMVCRepository<Categories> categories = new subnusMVCRepository<Categories>();
subnusMVCRepository<Post_Category_Map> postCategoryMap = new subnusMVCRepository<Post_Category_Map>();
var query = from c in categories.GetAll()
join pcm in postCategoryMap.GetAll() on c.CategoryId equals pcm.CategoryId
where pcm.PostId == postId
select new Category
{
Name = c.Name,
CategoryId = c.CategoryId
};
return query;
}
The issue is most likely in the implementation of the query provider.
pcm.PostId == 1
and
pcm.PostId == postId
actually have a big difference. In the expression tree the first is generated as a ConstantExpression which doesnt need to be evaulated.
With the second, the compiler actually generates an inner class here (this is the _DisplayClassX that you see). This class will have a property (will most likely be the same name as your parameter) and the expression tree will create a MemberAccessExpression which points to the auto-generated DisplayClassX. When you query provider comes accross this you need to Compile() the Lambda expression and evaluate the delegate to get the value to use in your query.
Hope this helps.
cosullivan
The problem is not the linq itself,
you need to be sure that the context or provider object is able to fetch the data.
try testing the
subnusMVCRepository<Categories> categories = new subnusMVCRepository<Categories>();
subnusMVCRepository<Post_Category_Map> postCategoryMap = new subnusMVCRepository<Post_Category_Map>();
objects and see if they are populated or if they behaving as required.
you may want to search the generated code for c__DisplayClass1 and see what you can see there. some times the generated code dose some weird things.
when you step into you code check the locals and the variable values. this may also give you some clues.
Edit : Have you tried to return a List<> collection ? or an Enumerable type?
Edit : What is the real type of the item and query may not be iterable

Resources