NHibernate.LINQ Supported Operators - linq

I'm trying to evaluate NHibernate.LINQ 1.0 without actually writing any code. Ayende has admitted that this version of LINQ support is subpar compared to EF, but for the life of me I can't seem to find a page that explains what's supported and unsupported in this implementation. For example, can I use Skip & Take? What can I not use?

You can check LINQ for NHibernate examples to see tests done by Ayende himself on what's implemented and what's not for this provider.
Some of those generally supported:
Anonymous type creation. new { Person = x.Name }
First(). query.First()
FirstOrDefault(). query.FirstOrDefault()
Single(). query.Single()
SingleOrDefault(). query.SingleOrDefault()
Aggregate(). query.Aggregate((x1,x2) => x1)
Contains(). query.Where(x => x.Name.Contains("Foo"))
StartsWith().
EndsWith().
Substring(). where db.Methods.Substring(e.FirstName, 1, 2) == "An"
Sub-queries. query.Where(x => x.Company.Id == 4)
Count(). query.Where(x => x.Relatives.Count > 0)
Any(). query.Any()
Take(). query.Take(10)
Skip(). query.Take(10).Skip(4)
OrderBy(). orderby x.Name descending
Replace(). AfterMethod = e.FirstName.Replace("An", "Zan"),
CharIndex(). where db.Methods.CharIndex(e.FirstName, 'A') == 1
IndexOf(). where e.FirstName.IndexOf("An") == 1
Problematic:
Group by
Joins
One of my own examples:
query = NSession.Session.Linq<Catalog>()
.Where(acc => acc.Company.Status == "A")
.Where(acc => acc.Id.StartsWith("12-536"))
.Where(acc => acc.Id.EndsWith("92") || acc.Id.EndsWith("67"))
.Take(10).OrderBy(acc => acc.Title);
If you're production application is using the latest stable build 2.1.2.4 like I am, you're stuck with what the NHibernate.Linq provider gives us until NHibernate 3.0 (trunk) gets a stable release and we feel safe enough to use it on major applications. Until then, I'm more than happy with a mixture of NHibernate.Linq and HQL.

The basic test of whether NHibernate can work with a Linq statement is whether you can serialize that statement's expression tree, then deserialize it in a different process and get the right answer. That means no external closures; the lambda must work only with what it creates or is given as a parameter.
Linq2NH 1.0, IIRC, also chokes when using members of the class that are not mapped, so if, for instance, you have a read-only calculated property like a special weighted or rolling average, you must map it to a DB column in order to reference it in the lambda (or recreate the logic in the lambda). This is because the expression tree will eventually be boiled down into SQL (through one of NH's intermediates; in 2.x it's ICriteria, in 3.x it's HQL) and if NH cannot take an expression and convert it 1:1 into a SQL expression that will evaluate successfully, it's just not going to work.
There is one special case: Linq2NH, IIRC, is smart enough to turn an IList.Contains() expression into an IN clause. The list must be defined within the lambda (like new[]{"1","2"}.Contains(m.ID)).

The blog post from Ayende is from May this year. A lot of things changed.
The NHiberante. Linq 1.0 linq provider is deprecated since about a year because of the new linq provider in the NHibernate Trunk. The new linq provider is not completely finished yet, but already very complete and usable for much more than the old linq provider. Things that do not work with the new linq provider are considered bugs and will be solved some day when reported.
You can use skip and take with the old and new linq provider. The current list of known issues can be found on NHibernate Jira. Other issues are unknown and all other features are already supported.

Related

MSCRM 2011 EntitCollection and LINQ empty resultset

I have an EntityCollection ec in C# which has been populated with all Accounts.
Now I want another List or EntityCollection from ec which has all the accounts with status active.
I am using Linq for the same.
But both form of LINQ returns a an empty result while ec has 354 number of records
var activeCRMEC = (from cl in ec.Entities
where cl.Attributes["statecode"].ToString()=="0"
select cl);
OR
var activeCRMEC = ec.Entities.Where(x => x.Attributes["statecode"].ToString() == "0");
Each time the resultset is empty and I am unable to iterate over it. And 300 or so accounts are active, I have checked.
Same thing happens when I use some other attribute such as name etc.
Please be kind enough to point out my mistake.
You can Generate Early Bound Classes to write Linq Queries.
or Else
You can Write Linq Queries Using Late Bound Using OrganizationServiceContext Class.
For Your Reference:
OrganizationServiceContext OrgServiceCOntext = new OrganizationServiceContext(service);
var RetrieveAll = OrgServiceCOntext.CreateQuery("account").
ToList().Where(w => (w.GetAttributeValue<OptionSetValue>("statecode").Value ==0)).Select(s=>s);
I'll give you a few hints, and then tell you what I'm guessing your issue is.
First, use early bound entities. If you've never generated them before, use the earlybound generator, you'll save yourself a lot headaches.
Second, if you can't use early bound entities, use the GetAttribute() method on the Entity class. It'll convert types for you, and handle null reference issues.
Your LINQ expressions look to be correct, so either the ec.Entities doesn't have any entities in it that match the criteria of "statecode" equaling 0, or you possibly have some differed execution occurring on your IEnumerables. Try calling ToList() on the activeCRMEC immediately after the LINQ statement to ensure that is not your issue.
The statecode is an OptionSetValue, you should cast it in this way
((OptionSetValue)cl.Attributes["statecode"]).Value == 0
or
cl.GetAttributeValue<OptionSetValue>("statecode").Value == 0
Both ways are valid and you should ask for the Value that it is an int.
Hope this can help you.

Linq IQueryable.Any() Usage

Below is my code . Please review it .
1. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId == Guid.Parse(host.ID) &&
p.Status == (int)DeployStatus.Deploying).AsEnumerable().Any();
This one works.
The following statements doesn't work.
2. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId == Guid.Parse(host.ID) &&
p.Status == (int)DeployStatus.Deploying).Any();//Error
The Exception is
An exception of type 'System.NotSupportedException' occurred in
Microsoft.Data.Services.Client.DLL but was not handled in user code
Additional information: The method 'Any' is not supported.
3. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId.ToString() == host.ID &&
p.Status == (int)DeployStatus.Deploying).AsEnumerable().Any();//Error
The Exception is
An exception of type 'System.NotSupportedException' occurred in
Microsoft.Data.Services.Client.DLL but was not handled in user code
Additional information: The expression (([10007].HostEnvironmentId.ToString() ==
"b7db845b-cec4-49af-8f4b-b419a4e44331") And ([10007].Status == 90)) is not supported.
The Deploys Class is the model which is built in the client proxy class of WCF Data service. I was using "add service reference" to create WCF client proxy class.
But as to the Generic List,
Supposed below code. it will works fine.
4.bool b=servers.Where(d =>
d.status == (int)Enums.ServerStatus.Deploying ||
d.status == int)Enums.ServerStatus.Unavailable).Any();
My question is
Why same way used in different Class got different result .(See the method 2 and method 4).
Why 2 and 3 don't work.
Hope someone can help me . Thanks
LINQ has a concept of 'providers'. When working with LINQ over different data sources, different things need to happen for identical LINQ queries depending on the data source.
For example, when you want to use LINQ to query a database, the LINQ query needs to be converted to an SQL query. When the data source is OData, the query needs to be converted to a URL. There are different providers for each, and each provider supports a different subset of LINQ operators and other language constructs. LINQ-to-SQL, Entity Framework and LINQ-to-NHibernate are three popular LINQ providers for database access.
In your case, you are using WCF Data Services which includes a LINQ provider for OData. Since in OData there is no way to express the .Any() LINQ operator, attempting to use it in a query with that provider throws an exception. By using .AsEnumerable() you're essentially saying to stop using the OData LINQ provider at that point and start using the LINQ-to-Objects provider (which isn't technically a provider, but conceptually you can think of it as one). That means only what comes before .AsEnumerable() will be converted to an OData query, causing to retrieve all the Deploy entities that match the .Where(), and after they have all been transferred to the client, the client will perform the .Any() by checking the number of Deploy entities it has received. This of course is bad if there are many such entities, it will cause unwanted transfer of data over the network when all you wanted is the server side (database probably) to check if there are any. Unfortunately, .Any() is not supported by OData 1.0 (I don't know about OData 2.0).
Also, OData may not support .ToString() either. You may need to compare the Guid structures directly, i.e. create a local variable that contains the GUID value you want to compare:
var g = Guid.Parse("b7db845b-cec4-49af-8f4b-b419a4e44331")`
And then in the query compare the GUIDs like so:
x.HostedEnvironment == g

cannot project entities in Linq 2 NHibernate

I'm working with NHibernate 2 in a .Net project and I'm using the Linq2NHibernate provider.
This simple query
var result = from d in session.Linq<Document>()
where d.CreationYear == 2010
select d.ChildEntity).ToList();
throws an exception telling me that is impossible to cast ChildEntity type do Document type.
Why is that?
I also tried to translate it in query methods, having
session.Linq<Document>()
.where(d=>d.CreationYear == 2010)
.select(d=>d.ChildEntity)
.ToList();
Isn't the select method supposed to project an IQueryble into a IQueryble, beeing TResult!=T ?
Try this:
var result = (from d in session.Linq<Document>()
where d.CreationYear == 2010
select new ChildEntityType
{ /* here just do a simple assignments for all ChildEntityType fields
d.ChildEntity */ } ).ToList();
Yes, this could look quite stupid, but linq2nhibernate sometimes behave very strange, when you try to select just an object.
The old Linq provider is extremely limited and has been unmaintained for several years.
I suggest that you upgrade to the latest stable NHibernate (3.2), which has a much better (and integrated) Linq provider.
can you try this:
session.Linq<Document>()
.Where(d=>d.CreationYear == 2010)
.Select(d=>d.ChildEntity)
.ToList<T>(); //where T is typeof(ChildEntity)

OrderBy("it." + sort) -- Hard coding in LINQ to Entity framework?

I have been trying to use dynamic LINQ to Entity in my application for specifying the OrderBy attribute at runtime. However when using the code as described in the majority of documentation:
var query = context.Customer.OrderBy("Name");
I received the following exception:
System.Data.EntitySqlException: 'Name' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly.
After much searching I found this MSDN page:
http://msdn.microsoft.com/en-us/library/bb358828.aspx
Which included the following code example:
ObjectQuery<Product> productQuery2 = productQuery1.OrderBy("it.ProductID");
This prompted me to change my code to the following:
var query = context.Customer.OrderBy("it.Name");
After this the code works perfectly. Would anyone be able to confirm that this is indeed the correct way to get OrderBy working with LINQ to Entity? I can’t believe that the framework would have been implemented in this way, perhaps I have overlooked something?
Thanks, Matt
The it.Name syntax is ESQL and is indeed specific to the EF. There are good reasons to use this sometimes (e.g., collation specifiers), but it's not what I normally do.
Usually I use standard LINQ expressions:
var query = context.Customer.OrderBy(p => p.Name);
You can also use System.Linq.Dynamic, if you download it from Code Gallery, and then your original query:
var query = context.Customer.OrderBy("Name");
...will work.
No nice way, so far
My answer to this question was to create a stored procedure which has parameter to control sorting.

LINQ multiple where clause

I have a course table which I need to search based on keywords typed in the search box.
Here is a sample query:
SELECT * FROM Courses WHERE
Title LIKE '%word%' OR Title LIKE '%excel%' OR
Contents LIKE '%word%' OR Contents LIKE '%excel%'
How can I convert this in LINQ where LINQ would dynamically generate WHERE statements based on each keywords.
I tried to user PredicateBuilder it works fine as long as the field is VARCHAR. For the "TEXT" fields the quotes are not generated thus causing compiler to give an error message. Here is the SQL generated by PredicateBuilder
SELECT [t0].[CoursesID], [t0].[Title], [t0].[Contents], [t0].[Active],
FROM [dbo].[Courses] AS [t0]
WHERE ([t0].[Title] LIKE '%word%') OR ([t0].[Contents] LIKE %word%) OR
([t0].Title] LIKE '%excel%') OR ([t0].[Contents] LIKE %excel%)
Notice there is no single Quote for the "Contents" field which is a Text field in the database.
Is there any easy way to build WHERE statement and attach it with query? Does anyone know how I can do this without PredicateBuilder?
Thanks in advance.
Since you are working w/ LINQ I suppose you are working against a LINQ-to-SQL data context right? I don't have a spare DataContext lying around to test this, but this should give you some ideas.
I don't know if it will work against data context though, but most of these are pretty basic stuff (chaining OR operator and Contains method call) so it shouldn't cause problem when the query translates to SQL.
First I create a custom function that would build my predicate:
Func<string, Func<DataItem, bool>> buildKeywordPredicate =
keyword =>
x => x.Title.Contains(keyword)
|| x.Contents.Contains(keyword);
This is a function which takes a single string keyword and then return another function which takes a DataItem and checks it against the keyword.
Basically, if you pass in "Stack", you'll get a predicate: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack").
Next, since there are many possible keywords and you need to chain it with an OR operation, I create another helper function to chain 2 predicates together with an OR
Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate =
(pred1, pred2) =>
x => pred1(x) || pred2(x);
This function takes 2 predicates and then join them up with an OR operation.
Having those 2 functions, I can then build my where predicate like this:
foreach (var word in keywords) {
filter = filter == null
? buildKeywordPredicate(word)
: buildOrPredicate(filter, buildKeywordPredicate(word));
}
The first line inside the loop basically checks if the filter is null. If it is, then we want a simple keyword filter built for us.
Else if the filter is not null, we need to chain existing filters with an OR operation, so we pass the existing filter and a new keyword filter to buildOrPredicate to do just that.
And then we can now create the WHERE part of the query:
var result = data.Where(filter);
Passing in the complicated predicate we've just built.
I don't know if this will different from using PredicateBuilder but since we are deferring query translation to the LINQ-to-SQL engine, there should not be any problems.
But as I said, I havn't tested it against a real data context, so if there's any problems you can write in the comments.
Here's the console app that I built to test: http://pastebin.com/feb8cc1e
Hope this helps!
EDIT: For a more generic and reusable version which involves properly utilizing the Expression Trees in LINQ, check out Thomas Petricek's blog post: http://tomasp.net/articles/dynamic-linq-queries.aspx
As predicate builder doesn't know the DB type of the property the Contains method is called on, I guess this might be a problem inside linq to sql. Have you tried with a normal query (not with predicate builder) and a TEXT column with Contains?

Resources