LINQ Dynamic Query Library - entity-framework-4

I am building an ASP.Net MVC 3 application with Entity Framework 4. When the two pieces of code below are executed, both variables (query1 and query2) have a return type of
System.Data.Objects.ObjectQuery<Asset.Model.Equipment>
Query1 uses a direct instance of the ObjectContext, however, Query2 uses a repository pattern, ie, it calls GetEquipment in EquipmentService, which in turns calls the same named method in Equipment Repository. Both the methods in the Service and Repository return
IQueryable<Equipment>
How, here's my question, how come query2 will only work when I include
using System.Linq.Dynamic;
At the top of my controller
using (AssetEntities context = new AssetEntities())
{
var query1 = context.Equipments
.OrderBy("it." + sidx + " " + sord)
.Skip(pageIndex * pageSize)
.Take(pageSize);
}
var query2 = equipService.GetEquipment()
.OrderBy(sidx + " " + sord)
.Skip(pageIndex * pageSize)
.Take(pageSize);
If I omitt System.Linq.Dynamic from my controller, I get an error within Query2 at
.OrderBy(sidx + " " + sord)
Which states
The type arguments for method 'System.Linq.Queryable.OrderBy<TSource,TKey>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource,TKey>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly
Does anyone know why query1 can work without having to use System.Linq.Dynamic, but that query2 needs it to execute?
Thanks Everyone.

In the first query context.Equipments has type ObjectQuery<Equipment>. The ObjectQuery<T> has the method OrderBy(string) which one need for .OrderBy("it." + sidx + " " + sord). So the first query work.
In the second query you use equipService.GetEquipment() of the type IQueryable<Equipment>. The IQueryable<T> has only extension method OrderBy with Expression<Func<T, TKey>> as the parameter instead of string. So to use OrderBy with IQueryable<Equipment> you have to write something like
equipService.GetEquipment().OrderBy(e => e.equipmentID)
but it is not what you can use. To you need another extension method, which can provide you the LINQ Dynamic Query Library in form System.Linq.Dynamic.
In many cases LINQ to Entities has many restrictions, but in your case it has more advantages as LINQ to SQL. So I recommend you to stay by LINQ to Entities in your case. I am sure that in the way you will receive better performance because of native support of all function directly in the Entity Framework which you use.
Because LINQ to Entities or ObjectQuery<Equipment> supports Where(string) method (to be exactly ObjectQuery.Where(string predicate, params ObjectParameter[] parameters) method) you can relatively easy implement filtering/searching in jqGrid. The usage of .Where can be
.Where("it.equipmentID < 100")
or
.Where("it.equipmentID < #maxId", new ObjectParameter ("maxId", 100))
for example (the usage of "maxId" instead of "#maxId" in the ObjectParameter is not typing error).
UPDATED: In "UPDATED" part of the answer you can find the example which shows how to implement filtering/searching in jqGrid based on the idea which I described above.

"it" is the default ObjectQuery.Name property value. In fact, when you are using the first query, you perform an implicit Entity SQL Order By clause, while in the second one you are using LINQ to Entities, and it needs System.Linq.Dynamic namespace to work correctly.

Related

HQL Injection/findAll with sorting

In my application, the query is being built by appending the first part(where clause) with the second part(order by) using a separate script like QueryBuilder.groovy and hence the order by part is prone to HQL injection which can't be sanitized by using Named Parameters. Therefore, I want to use findAll to retrieve a set of records by passing it a query and sorting and paging parameters separately. I saw an implementation like this:
domainClass.findAll(query,[namedParams],[max: 10, offset: 5])
When i passed sortColumn and sortDirection as named parameters, sortColumn worked fine but sortDirection didn't work. i need a way to either make sortDirection work as a named parameter or any other way which will combine 'sorting by direction' with the findAll result. Many people have suggested on various forums to just use the parameters directly as part of the query but it is unacceptable for my application as it will expose the query to HQL Injection.
Thanks in advance.
here is an example:
queryString = "FROM BookCatalog b WHERE b.bookNumber = :bookNumber"
this is passed to the QueryBuilder.groovy where something like this happens:
sort = "$params.sortColumn $params.sortDirection"
queryString.order(sort)
public void sort(String query){
this.query = this.query+" order by "+query
}
finally findAll retrieves the list of records:
def list = findAll(queryString,namedParams,queryParams)
so as the logic just appends the sorting parameters to the query string a potential hacker can do something like this:
bookCatalogView?offset=2&max=5&sortColumn=1,2,3 **or 1=1**
or
bookCatalogView?offset=2&max=5&sortColumn=1,2,3;**select * from whatever**
Don't concat strings, it's bad practice.
If you want to create complex queries, consider using createCriteria()
SomeDomainClass.createCriteria().list {
order("propName", "desc")
}
or, if you need more control, in the sessionFactory way:
query = sessionFactory.getCurrentSession().createCriteria(DomainClass.class)
query.addOrder(Order.asc("someField"))
query.addOrder(Order.desc("someotherField"))

How to query on calculated column using Breeze

I have two columns in my database table (dueDate & lateMinutes).
How do I create a Where predicate that adds lateMinutes to dueDate and compares the result to a given date (aGivenDate).
eg. .where(dueDate + lateMinutes > aGivenDate)
Any idea how to do this with a Breeze query where predicate?
Any help appreciated.
Regards,
Paul.
There is no standard query syntax supporting date arithmetic within breeze, so your best bet for this would be to use a named query with a parameter. i.e. So assuming that the name of your EntityType was say 'Schedule' something like this
On the client
var q = EntityQuery.from("SchedulesAfter")
.where(...) // this can be any valid where clause for the 'Schedule' type ( or can be omitted completely if you don't need additional query restrictions.
.withParameter( { givenDate: aGivenDate });
On the server
[HttpGet]
public IQueryable<Schedule> SchedulesAfter(DateTime givenDate) {
// This needs to return a valid IQueryable
// You will need to find the correct EF syntax to support your query here. You may need to use an EF function here instead.
return ContextProvider.Context.Schedules
.Where(s => DbFunctions.AddMinutes(s.DueDate ,s.LateMinutes) > givenDate);
}
In effect, you are calling a custom query method on the server with a parameter and then telling the server how to implement the query using this parameter.

Does breeze's withParameters method work with an OData service adapter?

I'm using OData with Entity Framework to select some records from a database. The records each have a path to a file with text content. In a single service call I'd like to be able to filter the DB records as well as filter the resulting objects based on the content of the files that the records point to. Because I can't mix LINQ to EF with LINQ to Objects, I believe the easiest way to accomplish this is to add an additional query parameter to the standard OData parameters that defines how to filter for file content after the standard odata filters have been applied.
Is looks like the entity query's "withParameters" method is the way to add a non-standard parameter but it doesn't seem to work with version 1.4.9 of breeze.
Am I doing something wrong or is there any intention to make this method work for the OData service provider?
As a workaround to this shortcoming, I've found that you can declare your entity to query with the parameters as part of the entity name, like so:
var entityId = 4;
var answerId = 6;
var entityToQuery = "MyEntity(EntityId=" + entityId + ",answerId=" + answerId + ")";
Then, build your breeze query:
var query = breeze.EntityQuery.from(entityToQuery);
This would map to an OData endpoint such as:
public IQueryable<MyEntity> GetMyEntity([FromODataUri] int entityId, [FromODataUri] int answerId)
{
}
No, you need to use the WebApi adapter. This is not a breeze shortcoming, it's a OData shortcoming because OData doesn't support this syntax.
However, the WebApi adapter does do everything you want and this is the Breeze default. Please see the docs for more information.

paginatedlist throwing an error "Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator."

I have looked here :
LINQ To SQL exception: Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains operator
and here :
LINQ to Populate a range
but I can't figure this one out. I have based my Task Manager on the NerdDinner project. Instead of FindUpcommingDinners I have a method below :
Public Function FindAllTeamTasks(ByVal TeamRole As String) As IQueryable(Of Task) Implements ITaskRepository.FindAllTeamTasks
Return From task In db.Tasks
Join usrs In System.Web.Security.Roles.GetUsersInRole(TeamRole)
On task.TaskAssignedToID Equals usrs
Order By task.InsertDateTime _
Select task
End Function
When the result is passed into the Paginatedlist as source it errors with the "Local sequence cannot ..." error. The answer might be here as well but I can't figure it out. Please help.
ASP.NET MVC2 LINQ - Repository pattern, where should the pagination code go?
You can't use the result of GetUsersInRole() in a join to a database table via LINQ to SQL. You can try this instead:
Dim usrs = System.Web.Security.Roles.GetUsersInRole(TeamRole)
Return From task In db.Tasks _
Where usrs.Contains(task.TaskAssignedToID) _
Order By task.InsertDateTime _
Select task
Whenever your are using Linq to SQL, your query has to be able to be translated into SQL to be run against your database. The Linq provider for Linq to SQL only knows how to do this for very certain operations that would occur outside the database. So, for instance, Linq to SQL has no idea what to do with
Join usrs In System.Web.Security.Roles.GetUsersInRole(TeamRole)
because GetUsersInRole is not (as far as Linq is concerned) a table in the database, but rather a collection in your application.
As the error message mentions, there is an "exception" in that, in some cases, Linq to SQL does know what to do with Contains (by turning it into IN in SQL, if I recall) for a collection that does not reside in the database. With that in mind, you might turn your query into something like (sorry it's in C#, but I hope you can translate):
string[] users = System.Web.Security.Roles.GetUsersInRole(TeamRole);
return db.Tasks
.Where<Task>(t => users.Contains<string>(t.TaskAssignedToID))
.OrderBy<Task, DateTime>(t => t.InsertDateTime)
.AsQueryable<Task>();
Now, if your join was there because TaskAssignedToID was a collection and not merely a string (I'm not familiar with the model you're using, sorry), you might instead need to do something like
string[] users = System.Web.Security.Roles.GetUsersInRole(TeamRole);
return db.Tasks
.Where<Task>(t => t.TasksAssignedToID.Any<string>(i => users.Contains<string>(i)))
.OrderBy<Task, DateTime>(t => t.InsertDateTime)
.AsQueryable<Task>();
I'm not 100% sure that Linq to SQL is fine with Any -- I work mostly with Linq to Entities -- but I hope this makes some sense.

Whats wrong with LINQ to EF?

I am using EF. This is my LINQ query
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST
select new Tuple<int, string>(c.CD, c.NAME)).ToList();
}
When i call GetList() it throws an exception : Only parameterless constructors and initializers are supported in LINQ to Entities
Instead when i rewrite this query:
List<Tuple<int, string>> lst = new List<Tuple<int, string>>();
var query= (from c in DALContext.MST
select new{c.CD, c.NAME});
foreach (var item in query)
{
lst.Add(new Tuple<int,string>(item.CD,item.NAME));
}
return lst;
It just works fine. Whats wrong with my first query???
The other answers are correct about what's going on, but I didn't see anyone mention the best way to make your code work: AsEnumerable()
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST.AsEnumerable()
select Tuple.Create(c.CD, c.NAME)).ToList();
}
The AsEnumerable method acts as a boundary between the code that should be translated into SQL and executed in the database server, and the code that should be executed in memory after we've gotten a response from the database. Putting it right after the table name tells EF to get all the records from the MST table, and then run the following code that creates tuples from the values that are returned.
I changed your new Tuple<int, string> into Tuple.Create mostly because I don't like typing generic type parameters any more than I have to.
LINQ to EF deals with queries a bit differently than LINQ to SQL. In LINQ to EF, you can not put a constructor with parameters in a LINQ expression, like you did here in the first bit of code:
from c in DALContext.MST
select new Tuple<int, string>(c.CD, c.NAME)
The constructor of Tuple is taking two parameters, and that is not allowed in LINQ to EF.
The reason is explained here:
In part this is a matter of wanting
LINQ to Entities to be more explicit
about the boundary between what parts
of your query execute on the server
and what part execute on the client.
With LINQ to SQL, for instance, it is
possible to write a LINQ query which
not only involves data from the server
and functions on the server but also
functions that can only be executed on
the client and to mix them in
together. The LINQ to SQL provider
will then do its best to untangle
things and execute the parts that it
can on the server and other parts on
the client. This is nice because it
is easy to just write whatever query
you want and if at all possible it
will work. On the other hand, it's
not so nice if you accidentally write
a query where the only part which can
execute on the server is the most
basic thing that returns all the data
in one or more tables and then have
all the filtering happen on the client
(with very nasty perf consequences).
With LINQ to Entities, the boundaries
are more explicit. When you write a
LINQ query against a LINQ to Entities
IQueryable implementation, the entire
query executes on the server, and if
some part of the query cannot be
executed on the server, then an
explicit boundary must be created with
something like ToQueryable() or
ToList(). Once that query is executed
and the data retrieved, then you can
use LINQ to Objects to further refine
the query if you so choose. This way
you explicitly know where your
boundaries are, and it's easier to
track down performance issues and the
like. One of the related limitations
is that the select statement in LINQ
to Entities can create anonymous types
or other types as long as they have a
default constructor and settable
parameters. This minimizes the chance
that the select statement has major
side effects.
Or you could just write
var query= (from c in DALContext.MST
select new{c.CD, c.NAME}).ToList().Select(x=>new Tuple(x.CD, x.NAME));
This has the advantage it only brings from the DB the two columns you need.
Your class needs to have a parameterless constructor for Linq to EF and you have to instantiate it like this:
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST
select new Tuple<int, string>(){CD = c.CD, Name = c.NAME}).ToList();
}
EDIT:
If you are not in the position to add a parameterless constructor to TUPLE (which is the case here as Tuple is not an class per se) then you have no choice with Linq to EF but to do this as a two step process:
public List<Tuple<int, string>> GetList()
{
List<MST> mstList = (from c in DALContext.MST
select c).ToList();
List<Tuple<int, string>> tupleList = new List<Tuple<int, string>>();
mstList.foreach(c => tupleList.add(new Tuple(c.CD, c.Name)));
return tupleList;
}

Resources