I need to enhance a function of C# code. I am new to C# but I have strong java background.
My job is to write a new query, like JDBC, to get data from database.
In the method below, I didn't see any SQL query.
what does this line mean ? is this similar to Hibernate hql ?
from p in Session.Query<MyObject>() select p
Thanks
code:
public IPagingList<MyObject> ReadMyObjectItems(int start, int limit, IList<Filter> filters)
{
var criteria = Session.CreateCriteria<MyObject>();
if (limit != -1)
{
criteria.SetMaxResults(limit);
criteria.SetFirstResult(start);
}
if (filters != null)
{
foreach (var filter in filters)
{
criteria.Add(Restrictions.InsensitiveLike(filter.Field, "%" + filter.Value + "%"));
}
}
IList<MyObject> report = criteria.List<MyObject>();
int total = (from p in Session.Query<MyObject>() select p).Count();
var pagedResults = new PagingList<MyObject> { List = report, Total = total };
return pagedResults;
}
(from p in Session.Query<MyObject>() select p).Count();
We are simply getting a count of all the objects.
Refactor this to
Session.Query<MyObject>().Count(). It's easier to read and in this case LINQ is unnecessary.
It's Linq syntax. A powerful form of querying incorporated into .Net and different .Net frameworks.
In your case it seems you are using NHibernate and that line is using NHibernate Linq. (But the surrounding code is using Criteria.)
For composing queries in NHibernate you have the options of using
Linq
HQL
Criteria
which all have different pros and cons.
Either will be translated into SQL by NHibernate. The actual result will be fetched when you try to access it such as for instance reading an item in the result or converting the result to a List etc.
It seems that project is using an ORM like NHibernate. You can get more details here : http://nhibernate.info
Related
I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.
Hi i have this query...
List<VisitorsVo> lstVisitors = new List<VisitorsVo>();
var predicate = ReturnPredicateForVisitors(p_htVisitor);
if (predicate != null)
{
lstVisitors = (from n in context.TBL_VISITORs.Where(predicate)
select new VisitorsVo
{
VisitId = n.VISIT_ID,
VisitorName = n.VISITOR_NAME,
ResidentDuration = n.ENQUIRY_PATIENT_DURATION.Split(';')[0] + " " + n.ENQUIRY_PATIENT_DURATION.Split(';')[1],
}).ToList();
}
but i am getting Unrecognized expression node ArrayIndex error how can i overcome...
here i am checking condition in where using predicates....
It looks like this is LINQ to Entities, not plain LINQ. Is your context an Entity Framework or LINQ to SQL context?
If so, then LINQ to Entities/SQL will try to translate this expression into SQL, which it cannot do if it contains function calls it doesn't know or that have no SQL equivalent.
My money is on the use of Split(); I'll bet that LINQ to Entities barfs on that.
What you can do is modify your query to get rid of the Split(), and afterward you can query lstVisitors (which is now just an in-memory data structure, it has no link to Entity Framework) and use Split() there.
If you need to split a column that means you can hold that data in two separate columns.
If I were you I'd go to the database design and convert ENQUIRY_PATIENT_DURATION into 2 columns and do it properly. Your code seems very error prone.
I have the following function (which is hosted in a WCF service, if that matters):
public List<IceVsRepositoryFile> GetRepositoryFilesByRepositoryId(int repId)
{
var entity = new IceVSEntities();
var files = from p in entity.Files where p.RepositoryId == repId select p.FileId;
List<long> iList = files.ToList();
var repFiles = from p in entity.RepositoryFiles where iList.Contains(p.FileId) select p;
if (!repFiles.Any())
return null;
var retFiles = repFiles.ToList().Select(z => new IceVsRepositoryFile
{
FileId = (int)z.FileId,
RollbackFileId = (int)z.RollbackFileId,
UserId = (int)z.UserId,
FileContents = z.FileContents,
ChangeDescription = z.ChangeDescription
}).ToList();
return retFiles;
}
When I run this function I am getting the following an error that says "LINQ to Entities does not recognize the method 'Boolean Contains(Int64)' method and this method cannot be translated into a store expression.
I understand why I am getting the error message. My question is, how can I rewrite my query to make this work as expected? My backend database, if it matters, if SqlLite 3. I am using .NET 3.5.
The contains you used is for List, it's not in IEnumerable so it can't be converted to corresponding sql query. Instead you can use Any, ... like:
iList.Any(x=>x == p.FileId) (or use related property)
Also instead of doing:
List<long> iList = files.ToList();
use files.Any... in your query to prevent from too many fetching from DB. Actually use IEnumerable functions instead of List functions.
I believe a join can do this:
public List<IceVsRepositoryFile> GetRepositoryFilesByRepositoryId(int repId)
{
var entity = new IceVSEntities();
var repFiles = from file in entity.Files where file.RepositoryId == repId join repFile in entity.RepositoryFiles on repFile.FileId equals file.FileId select repFile;
var retFiles = // as before
return retFiles;
}
Do you have a relationship between Files and RepositoryFiles? If so, it would be easier to do something like this:
var repFiles = from p in entity.RepositoryFiles where p.File.RepositoryId == repId select p;
This will avoid the problems with being unable to translate the query to SQL.
I am slowly porting over an app from MySQL to use Linq2Sql - but one query has stumped me a bit.
SELECT * FROM Pages WHERE DomainID = #reportid AND (PageContent REGEXP 'display:[ \t]*none') > 0 ORDER BY URL ASC
Any ideas on how I would write something like this with Linq2SQL? Its the REGEXP bit thats got me stumped?
There is no way built in to LINQ to SQL, but you have a couple of other choices. The first is to load your strings in as in-memory objects which you can apply Regex functions to. I'm not a big fan of this since it looks like you're potentially getting some very big strings to match against.
The second option is to leverage SQL CLR as described here. This effectively lets you create a stored procedure that gets linked to a CLR method that you create. Whenever you call the method in a LINQ to SQL context, it gets converted to a stored procedure call. Then you use a query like this:
var q = from p in context.Pages
where p.DomainId == reportId &&
RegExMatch(p.PageContent, "display\:[ \t]*none")
select p;
Why not use LINQ to return items that match on reportid and that contain 'display:', to minimise the amount of data being returned from the server, and then use regex on client side to filter that list down?
var query = Pages.Where( p => p.DomainId == 1 && p.PageContent.IndexOf("display:") > 0).OrderBy( o => o.URL );
var regex = new Regex(#"display\:[\t]*none");
foreach (var page in query)
{
if( regex.IsMatch(page.PageContent) )
{
// Do whatever...
}
}
I would like to return a set of entities who has and ID that is contained in a list or array of IDs using LINQ and Data Services. I know how to this using LinqToEF but I am at a loss how to this with Data Services or using OData query conventions for that matter.
My thought is that I would do something like:
int[] intArray = {321456, 321355, 218994, 189232};
var query = (from data in context.Entity
where intArray.contains(data.ID)
select data);
Is there any way to accomplish using Data Services / OData? I know I could probably hack it with a Service Operation but I would prefer not to do that.
Cheers.
Currently OData (the underlying protocol) doesn't support the Contains operation. So that's why the client library does not translate the above query.
People are basically using two ways to overcome this limitation:
1) Use service operations as you noted.
2) Construct a where clause dynamically which uses simple comparisons to compare the value to each item from the array. So if the array contains 1, 2, 3, the where would be data.ID == 1 || data.ID == 2 || data.ID == 3
The #2 solution is nice because it's a client side only change. The downside is, that it only works for small arrays. If the array contains too many items the expression gets too long and that leads to all kinds of troubles.
The #1 solution doesn't have the size problem, but you need to provide the operation on the server.
Here is my realization of WhereIn() Method, to filter IQueryable collection by a set of selected entities:
public static IQueryable<T> WhereIn<T,TProp>(this IQueryable<T> source, Expression<Func<T,TProp>> memberExpr, IEnumerable<TProp> values) where T : class
{
Expression predicate = null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
bool IsFirst = true;
// Create a comparison for each value eg:
// IN: t => t.Id == 1 | t.Id == 2
MemberExpression me = (MemberExpression) memberExpr.Body;
foreach (TProp val in values)
{
ConstantExpression ce = Expression.Constant(val);
Expression comparison = Expression.Equal(me, ce);
if (IsFirst)
{
predicate = comparison;
IsFirst = false;
}
else
{
predicate = Expression.Or(predicate, comparison);
}
}
return predicate != null
? source.Where(Expression.Lambda<Func<T, bool>>(predicate, param)).AsQueryable<T>()
: source;
}
And calling of this method looks like:
IQueryable<Product> q = context.Products.ToList();
var SelectedProducts = new List<Product>
{
new Product{Id=23},
new Product{Id=56}
};
...
// Collecting set of product id's
var selectedProductsIds = SelectedProducts.Select(p => p.Id).ToList();
// Filtering products
q = q.WhereIn(c => c.Product.Id, selectedProductsIds);
Thank you men you really helped me :) :)
I did it like Vitek Karas said.
1) Download the Dynamic query library
Check this link
No need to read it just download the Dynamic query library
2)Check the project named DynamicQuery. In it you will find a class named Dynamic.cs . Copy It to your project
3)Generate your project( If you are using silverlight an error that say ReaderWriterLock is not found will appear. Don't be affraid. Just comment or delete the lines that make errors( there is just 6 or 7 lines that make errors) )
4) All done you just need now to write your query
Example: ordersContext.CLIENTS.Where(" NUMCLI > 200 || NUMCLI < 20");
All done. If you have to use the 'Contains' method you just to write a method that iterate over your array and return the string that your request will use.
private string MyFilter()
{ string st = "";
foreach(var element in myTab)
{
st = st + "ThePropertyInTheTable =" + element + "||";
}
return st;
}
I hope you understand me and that i helped someone :)