I'm trying to use dynamic linq to obtain a subset of people from a database using Entity
Framework (EF). I'm running into a problem when using the contains operation. Here is the entity
for the People table:
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
Here is a query that works successfully.
var people = personContext
.People
.OrderBy("id asc")
.Skip(0)
.Take(5)
.ToList();
Notice that I'm using dynamic linq in the OrderBy method. However, when I try to apply
filtering, I get an exception.
var people = personContext
.People
.Where("id.Contains(15)")
.OrderBy("id asc")
.Skip(0)
.Take(5)
.ToList();
What I'd like to get back is a subset of people with ids that contain the substring "15", such as:
"015", "115", "151", "152", etc.
When I execute the code, I get the following error.
System.Linq.Dynamic.ParseException was unhandled by user code
Message=No applicable method 'Contains' exists in type 'String'
What is the syntax for determining if the Id field contains the string "15"?
What is the syntax for determining if the Id field contains the string "15"?
Well, definitely not .Where("id.Contains(15)") which is trying to invoke the method Contains with numeric value 15.
According to the documentation, you can use either a string literal:
.Where("id.Contains(\"15\")")
or substitution values:
.Where("id.Contains(#0)", "15")
I feel misconception here... You are not supposed to use LINQ like this.
As a start you need to invoke the overloads that accept lambdas; then you specify the property in the lambda and if its a string you invoke Contains on it. Like so:
var people = personContext
.People
.Where(p => p.Id.Contains("15"))
.OrderByDescending(p => p.Id)
.Skip(0) // You don't need this line.
.Take(5)
.ToList();
The EF itself will do the heavy lifting and translate these pure C# codes into the correct SQL statements.
You can't use Contains in the LINQ query. Instead you can try this
var people = (from p in personContext.Set<People>()
where p.Id.Contains("15")
orderby p.Id
select p).Skip(0).Take(5).ToList();
Related
I have two models:
public class Employee
{
public int Id { get; set; }
public IList<Skill> { get; set; }
}
public class Skill
{
public int Id { get; set; }
}
And I have filter with list of skill ids, that employee should contain:
public class Filter
{
public IList<int> SkillIds { get; set; }
}
I want to write query to get all employees, that have all skills from filter.
I tried:
query.Where(e => filter.SkillIds.All(id => e.Skills.Any(skill => skill.Id == id)));
And:
query = query.Where(e => e.Skills
.Select(x => x.Id)
.Intersect(filter.SkillIds)
.Count() == filter.SkillIds.Count);
But as a result I get exception says that query could not be translated.
It is going to be a difficult, if not impossible task, to run a query like this on the sql server side.
This is because to make this work on the SQL side, you would be grouping each set of employee skills into a single row which would need to have a new column for every skill listed in the skills table.
SQL server wasn't really made to handle grouping with an unknown set of columns passed into a query. Although this kind of query is technically possible, it's probably not very easy to do through a model binding framework like ef core.
It would be easier to do this on the .net side using something like:
var employees = _context.Employees.Include(x=>x.Skill).ToList();
var filter = someFilter;
var result = employees.Where(emp => filter.All(skillID=> emp.skills.Any(skill=>skill.ID == skillID))).ToList()
This solution works:
foreach (int skillId in filter.SkillIds)
{
query = query.Where(e => e.Skills.Any(skill => skill.Id == skillId));
}
I am not sure about it's perfomance, but works pretty fast with small amount of data.
I've also encountered this issue several times now, this is the query I've come up with that I found works best and does not result in an exception.
query.Where(e => e.Skills.Where(s => filter.SkillIds.Contains(s.Id)).Count() == filter.SkillIds.Count);
I am currently trying to build a web API that interfaces with MongoDB using the C# driver.
The GET route uses several optional parameters that dynamically generate the LINQ query based on which parameters are in the request. The issue I am having is when filtering by _id it always fails to return data; these attempts include _id.ToString() as well as a simple _id == id comparison. I have tried several methods of comparing the id parameter and the document _id and none of them have worked. Having no where clause at all, or filtering by any other fields in the document in any combination all return data as expected.
This is currently what the snippet looks like:
var testId = new ObjectId(id);
var result = collection.AsQueryable<Terrain>()
.Where(t => t._id.Equals(testId))
.Select(t => t);
return Json(result);
This will return an empty result []. I can only assume I am misunderstanding something about the way _id is being stored being the database and queried here. For testing purposes, I am getting the value of the parameter id by copying the _id value out of Robo 3T.
Please share your 'Terrain' model
Please note that you are initiating an empty testId object and you are trying to match your documents _id to an empty object. You should set the testId with the object identifier which you would like to find (for example: var testId = new ObjectId("12345") )
Try to remove the .Select at the end (it's actually projecting the document results and if you would like to have the full document - it's not needed)
In addition, add at the end of the query .First() -or- .FirstOrDefault()
It should look something like:
var result = collection.AsQueryable()
.Where(t => t._id == testId)
.FirstOrDefault();
Another approach is just using a simple .Find():
var result = collection
.Find<Terrain>(t => t._id == testId)
.FirstOrDefault();
_id property needed to be decorated as follows. Database was storing _id as a string and not ObjectID.
namespace RIDBAPI.Models
{
public class Terrain
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string _id { get; set; }
[BsonElement("prefab")]
public string prefab { get; set; }
[BsonElement("colour")]
public string colour { get; set; }
[BsonElement("v3")]
public decimal[] v3 { get; set; }
}
}
Is it possible to replicate the following SQL query in NHibernate?
SELECT Name, COUNT(Name)
FROM Table
GROUP BY Name
Unfortunately I'm not able to get NHibernate to execute this as a raw sql query either as it is not permitted by my current employer.
I've seen examples of returning a count of linked entities but not a count of data in the same table.
I've currently got this working by using two queries. One to get a distinct list of names and one to get the count for each name. I'd like to optimise this to a single database call.
Thanks in advance for any help you can give!
We can do it like this:
// this would be our DTO for result
public class ResultDTO
{
public virtual string Name { get; set; }
public virtual int Count { get; set; }
}
This would be the query
// here we declare the DTO to be used for ALIASing
ResultDTO dto = null;
// here is our query
var result = session.QueryOver<Table>()
.SelectList(l => l
.SelectGroup(x => x.Name).WithAlias(() => dto.Name)
.SelectCount(x => x.Name).WithAlias(() => dto.Count)
)
.TransformUsing(Transformers.AliasToBean<ResultDTO>())
.List<ResultDTO>();
I'm using Entity Framework 6 with MVC and I'm using this query in the controller:
using (var context = new MyDbContext())
{
IQueryable<ContentArticle> query;
query = context.ContentArticle.Where(
c => c.ContentArticleSubdivisions.Subdivisions.Name == subdivision
The compiler is complaining that
'System.Collections.Generic.ICollection<MySite.DAL.Models.ContentArticleSubdivision>' does not contain a definition for 'Subdivisions'
However my content article model includes this property:
public virtual ICollection<ContentArticleSubdivision> ContentArticleSubdivisions { get; set; }
and my content article subdivision model includes this:
public partial class ContentArticleSubdivision
{
...
public virtual ICollection<Subdivision> Subdivisions { get; set; }
}
so what am I doing wrong? I think I need to modify the query so that it's looking up all possible Subdivisions that could be contained in the ContentArticleSubdivisions collection?
You need to use some sort of collection based method to do that. I'm not exactly sure what you are trying to query for here. I'm going to assume you want all articles where a subdivision exists matching that name.
query = context.ContentArticle.Where(
c => c.ContentArticleSubdivisions.Any(cs => cs.Subdivisions.Any(s => s.Name == subdivision))
);
You can also simplify this using the query comprehension syntax like so
query = from ca in context.ContentArticle
from cas in ca.ContentArticleSubdivisions
from s in cas.Subdivisions
where s.Name == subdivision
select ca;
Utilizing NHibernate I am attempting to use a lambda expression to retrieve objects based on the status and values between a parent child relationship. AbstractWorkflowRequestInformation has a collection of WorkflowRequestInformationAction. Each of the two classes have their own Status properties. To illustrate here are the abbreviated classes as they relate to this query:
public class AbstractWorkflowRequestInformation
{
public virtual RequestStatus RequestStatus { get; set; }
public virtual IEnumerable<WorkflowRequestInformationAction>
WorkflowRequestInformationActionList { get; set; }
}
public class WorkflowRequestInformationAction
{
public virtual ActionStatus Status { get; set; }
public virtual string RoleIdentifier { get; set; }
public virtual string RoleName { get; set; }
}
Given this relationship I want to retrieve AbstractWorkflowRequestInformation objects based on a List<KeyValuePair<string, string>> called roles. I realize that the exception is being caused by a lack of parsing of the Any(...) extension method, but I am unsure of alternative queries. Thus far all permutations on the below query have caused the same or similar exceptions:
public IEnumerable<IRequestInformation> GetRequestsPendingActionBy(
List<KeyValuePair<string, string>> roles)
{
var results = GetSession().Query<AbstractWorkflowRequestInformation>()
.Where(r => r.RequestStatus == RequestStatus.Pending
&& r.WorkflowRequestInformationActionList
.Any(a => ActionStatus.Pending == a.Status
&& roles.Any(kp => kp.Key == a.RoleName
&& kp.Value == a.RoleIdentifier)))
.ToList();
return results;
}
The ultimate goal is to retrieve only those AbstractWorkflowRequestInformation objects which are pending and have a pending WorkflowRequestInformationAction matching a KeyValuePair in the roles enumerable.
I am not wedded to using a lambda expression as this expression has already grown unwieldy, if there's a more elegant ICriteria expression I am all ears. What are my options to restrict my results based upon the values in my roles List<KeyValuePair<string, string>> but prevent the "Specified method is not supported" exception?
I think this would get same results...
WorkflowRequestInformationAction actionAlias = null;
var q = GetSession().QueryOver<AbstractWorkflowRequestInformation>()
.Inner.JoinAlias(x => x.WorkflowRequestInformationActionList,
() => actionAlias)
.Where(x => x.RequestStatus == RequestStatus.Pending)
.And(() => actionAlias.Status == ActionStatus.Pending);
var d = Restrictions.Disjunction();
foreach(var kvp in roles)
{
d.Add(Restrictions.Where(() => actionAlias.RoleName == kvp.Key
&& actionAlias.RoleIdentitifier == kvp.Value));
}
q.And(d).TransformUsing(Transformers.DistinctRootEntity);
var results = q.List();
You could probably take a similar approach with NH Linq. I'm more comfortable with QueryOver/Criteria though.
The LINQ provider in NHibernate is not fully supported, you are trying to execute an extension method on a part of the expression tree that is not parsed from the provider.
This post might help you solve the problem. Be sure to checkout the related posts.
Also see the post from Fabio Maulo on NHibernate LINQ provider extension.