How to get an object from the database with NHibernate? - c#

I'm trying to get an object back from an NHibernate query.
My method is as follows:
public Site GetSiteByHost(string host)
{
var result = _session.CreateCriteria<Site>()
.Add(SqlExpression.Like<Site>(g => g.URLName, host));
return result;
}
the problem is, result is a type of HNibernate.ICriteria.
How can I get this to return a Site object?
If I was doing this with LINQ to SQL it'd be something like .FirstOrDefault() but that's not available with NHibernate... or is it?!?!

You need to first execute the query (by calling List<T>() on the criteria) before calling FirstOrDefault. Notice that this query might return multiple objects:
IEnumerable<Site> sites = _session
.CreateCriteria<Site>()
.Add(SqlExpression.Like<Site>(g => g.URLName, host))
.List<Site>();
And you could take the first one:
Site result = sites.FirstOrDefault();
or directly:
public Site GetSiteByHost(string host)
{
return _session
.CreateCriteria<Site>()
.Add(SqlExpression.Like<Site>(g => g.URLName, host))
.List<Site>()
.FirstOrDefault();
}

I think you can put a .List<Site>() on the end, and then do the .FirstOrDefault() on it.

I believe .UniqueResult() is what you're after...
from the docs:
Convenience method to return a single
instance that matches the query, or
null if the query returns no results.

You can use linq with NHibernate. It is in the NHibernate.Linq namespace in the trunk of NHiberante.
return session.Query<Site>().FirstOrDefault(site => site.UrlName.Contains(host));
When you prefer the criteria API over Linq, you have to use result.SetMaxResults(1).UniqueResult() to create something equal to IQueryable.FirstOrDefault

Related

Method Chaining in EF6 Doesn't Output the correct SQL

I have the following methods:
private IEnumerable<CTNTransactionsView> RetrieveCTNTransactionsNotInTLS() {
IQueryable<int> talismanIdCollection = this._cc.TLSTransactionView.Select(x => x.kSECSYSTrans);
return this._cc.CTNTransactionView
.Where(x => !talismanIdCollection.Contains(x.kSECSYSTrans));
}
public IEnumerable<CTNTransactionsView> RetrieveCTNTransactionsNotInTLSPast24Hours() {
DateTime previousDate = DateTime.Now.Date.AddDays(-1.0);
return this.RetrieveCTNTransactionsNotInTLS()
.Where(x => x.dSECSYSTimeStamp >= previousDate);
}
public IEnumerable<CTNTransactionsView> RetrieveCTNTransactionsNotInTLSPast24HoursVersionTwo() {
DateTime previousDate = DateTime.Now.Date.AddDays(-1.0);
IQueryable<int> talismanIdCollection = this._cc.TLSTransactionView
.Select(x => x.kSECSYSTrans);
return this._cc.CTNTransactionView
.Where(x => !talismanIdCollection.Contains(x.kSECSYSTrans))
.Where(x=> x.dSECSYSTimeStamp >= previousDate);
}
For some reason, the SQL output generated by Entity Framework 6 does not match the results.
The RetrieveCTNTransactionsNotInTLSPast24HoursVersionTwo() method will properly give a SQL Output Statement that has the following:
select ...... from ... where ... AND ([Extent1].[dSECSYSTimeStamp] >= #p__linq__0)}
The other one does not have the filter for the dSECSYSTimeStamp when I View the SQL Statement Output.
The methods I am comparing are the RetrieveCTNTransactionsNotInTLSPast24Hours() and the RetrieveCTNTransactionsNotInTLSPast24HoursVersionTwo().
I have compared the SQL using VS as well as attaching a Debug.Writeline() to the Database.Log in the context.
From debugging and looking at the SQL output, one seems to contain the date filter whereas the other doesn't and yet they both provide the correct result.
I have tried looking at the SQL (by breakpointing and seeing the output) from using the following:
System.Diagnostics.Debug.WriteLine("Running first method");
var result = this.repo.RetrieveCTNTransactionsNotInTLSPast24Hours();
var count = result.Count();
System.Diagnostics.Debug.WriteLine("Running Second method");
var resultTwo = this.repo.RetrieveCTNTransactionsNotInTLSPast24HoursVersionTwo();
var count2 = resultTwo.Count();
I am using EF 6.0.
Note: The results are the same as both do exactly the same thing and output the same result. However, I am curious and would like to understand why the SQL Generated isn't the same?
The issue is you are returning an IEnumerable from your method. If you do this, you force SQL to run the query (unfiltered) and then use C# to then run the second query. Alter your internal query to return an IQueryable. This will allow the unexecuted expression tree to be passed into the second query, which will then be evaluated when your run it.
i.e.
private IQueryable<CTNTransactionsView> RetrieveCTNTransactionsNotInTLS()
You should then get the same SQL.
I think, you just define a query expression, but not use it immediately, in this time, I will add .ToList() in the Linq expression's end, but you have to change method's return type into a right type like List<kSECSYSTrans> before this operation. I just seldom use the type IEnumerable.

c# contains method usage

I'm adding a "search" functionality to a web app I'm working on and I have the following action method:
public PartialViewResult SearchEmployees(string search_employees)
{
var employeeList = _db.Employees.ToList();
var resultList = employeeList.Where(t => t.FirstName.Contains(search_employees)).ToList();
return PartialView(resultList)
}
here I'm trying to filter out all employees that have a first name that contains the search string, however I keep getting a null list. Am I using the lambda expression wrong?
another question, is .Contains case sensitive? (I know in java theres .equals and .equalsIgnoreCase, is there something similar to this for .Contains?)
The problem here was the .ToList() in the first line.
.NET's string.Contains() method is, by default, case sensitive. However, if you use .Contains() in a LINQ-to-Entities query, Contains() will follow the case sensitivity of the database (most databases are case insensitive).
When you called .ToList() in the first line, it pulled down all of your data from the database, so the second line was doing an ordinary .NET .Contains(). Not only did it give you unexpected results, it's also terrible for performance, so please make a point to use a query before you use .ToList() (if you even use .ToList() at all).
public PartialViewResult SearchEmployees(string search_employees)
{
var employeeList = _db.Employees;
var resultList = employeeList.Where(t => t.FirstName.Contains(search_employees))
.ToList();
return PartialView(resultList)
}
Can you try the following code?
public PartialViewResult SearchEmployees(string search_employees)
{
var employeeList = _db.Employees.ToList();
var resultList = employeeList;
if(!String.IsNullOrEmpty(search_employees))
resultList = employeeList.Where(t => t.FirstName.Contains(search_employees)).ToList();
return PartialView(resultList)
}
Thanks,
Amit

Can't get deferred LINQ statements to work in nHibernate

Trying to write dynamic queries using the LINQ provider for NHibernate, but I am having issues. My understanding was that LINQ queries were deferred until called, (i.e. with ToList()), so I have the following code:
string[] filteredIds = new[] { "someIdNotInUse"};
var result = _products
.GetAll()
.Skip(0)
.Take(10);
if (filteredIds != null)
{
result.Where(x => x.Child1.Child2.Any(z => filteredIds.Contains(z.Child3.Id)));
}
var r = result.ToList();
The Where filter in the conditional block is not applied; when I run .ToList, I get records where I expect none. However, if I remove the where filter and append it directly to the _products call, it works as expected. Am I misunderstanding how the LINQ provider works? How is creating a query like this possible, without rewriting the query for every possible filter condition and combination?
Methods in LINQ don't affect the object they're called on - they return a new object representing the result of the call. So you want:
if (filteredIds != null)
{
result = result.Where(...);
}
(Think of it as being a bit like calling Replace or Trim on a string - the string is immutable, so it's only the return value which matters.)

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression

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.

Linq Intersect(...).Any() inside of a Where clause throws NotSupportedException

Whenever the tags argument is not empty I get a NotSupportedException:
Local sequence cannot be used in LINQ to SQL implementation of query operators except the
Contains() operator.
[WebMethod]
public static object GetAnswersForSurvey(string surveyName, int? surveyYear, IEnumerable<string> tags, IEnumerable<string> benchmarks)
{
IQueryable<DAL.Answer> results = new DataClassesDataContext().Answers
.OrderBy(a => a.Question.Variable);
if (!String.IsNullOrEmpty(surveyName)) results = results.Where(a => a.Survey.Name == surveyName);
if (surveyYear.HasValue) results = results.Where(a => a.Survey.Year == surveyYear.Value);
if (tags.Any()) results = results.Where(answer => answer.Question.Tags.Select(t => t.Label).Intersect(tags).Any());
if (benchmarks.Any()) results = results.Where(answer => benchmarks.Contains(answer.Question.BenchmarkCode));
return results.Select(a => new {
a.Question.Wording,
a.Demographic,
Benchmark = a.Question.BenchmarkCode,
a.Question.Scale,
a.Mean,
a.MEPMean,
a.NSSEMean
});
}
I understand it may not be possible to do it the way I'm trying. If it is impossible, can anyone offer any alternatives?
A number of the general purpose Linq to Object methods are not support within Linq to SQL.
http://msdn.microsoft.com/en-us/library/bb399342.aspx
An alternative might be to complete several sub-queries against sql and then perform your intersection operations as Linq to Objects
I think the reason is that the implementation of System.Data.Linq.Table.Intersect returns an IEnumerable, which in turn does not implement a parameterless version of Any().

Categories