Dynamic LINQ cast to nullable - c#

I'm using Dynamic LINQ with NHibernate.
Simple example demonstrates my problem:
var q = sess.Query<User>().Select("new (Id, Login, Person.Id as PersonId)");
throws NullReferenceException when where are Users in database with User.Person == null.
This solves the problem:
var q = sess.Query<User>().Select(new { Id, Login, PersonId = (long?)Person.Id });
but I need something like
var q = sess.Query<User>().Select("new (Id, Login, (long?)Person.Id as PersonId)");
because select expression is generated dynamically.
Unfortunately dynamic LINQ does not understand cast to (long?) :(
What should I have to do?
Thanks in advance!
==== Edit ====
Ok, I understood two things:
1. Dynamic LINQ does not know about long, only Int64.
2. Cast is not '(Int64)something' but 'Int64(something)'.
So whole my code should be
sess.Query<User>().Select("new (Id, Login, Int64?(Person.Id) as PersonId)");
But it still does not solve my whole problem because NHibernate now fails with Could not execute query[SQL: SQL not available] exception and NullReference inner exception.

Finally I have mastered the problem. I studied Dynamic.cs and found that the Dynamic LINQ's syntax is very specific. Particularly the string expression "SomeType(SomeExpression)" generates cast expression only if SomeType has no constructor with one parameter. In other case it generates "new SomeType(SomeExpression)". I considered it as a mistake and slightly modified the Dynamic.cs.
Now in my example the cast can be made as 'Int64? Person.Id'. It is not a "normal" cast syntax but it works for me like a charm. Dynamic ExpressionParser is pretty good but unfortunately is not well adapted for C# cast syntax.
Also I registered in Dynamic.cs short type names like "long", "int" etc.
The modified source is downloadable here: http://1drv.ms/1cRJtSP .

Related

Converting for-loop to a linq query

I like to think of myself as pretty good with LINQ, but every now and then I try to accomplish something that I just can't get to work. I'd like to convert a SPListItemCollection to a dictionary so I can use the key to look up a value without the need for a LINQ query each time:
var formsConfigItems = new Dictionary<string, string>();
foreach (SPListItem item in list.GetItems(query))
formsConfigItems.Add(item.Title, (item["Value"] == null ? string.Empty : item["Value"].ToString()));
This works, but I was hoping to do it in a cleaner fashion, using LINQ. (not a big deal but I like to use LINQ over for-loops whenever possible, although it's the same thing behind the scenes.
I tried to do something like this:
var formsConfigItems = (from SPListItem i in list.GetItems(query)
select new { i.Title, i["Value"].ToString() }).ToDictionary<string, string>(k=>k.Key, k=>k.Value);
But that doesn't seem to work. If I try to use a lambda expression on list.GetItems(query), I'm not given the option to use .Where or any LINQ commands (which is weird because it is an SPListCollection)
Thanks in advance.
Try:
var formsConfigItems = list.GetItems(query)
.Cast<SPListItem>()
.ToDictionary(item => item.Title,
item => Convert.ToString(item["Value"]));
To answer your queries:
If I try to use a lambda expression on list.GetItems(query), I'm not
given the option to use .Where or any linq commands (which is weird
because it is an SPListCollection)
That's because SPListCollection is an "old-school" collection that implements IEnumerable but not IEnumerable<T>, so C# / LINQ (at compile-time anyway) can't tell what type of items it contains. The Cast<SPListItem>() call helps work around this issue - it turns an IEnumerable into an IEnumerable<T>, allowing the type-algebra to work out at compile-time . Your for loop doesn't have this issue since you explicitly specify the type of the loop variable - the compiler inserts a cast on your behalf for each item in the sequence.
I tried to do something like this (query expression). But that
doesn't seem to work.
That's because you are not constructing the anonymous type instance correctly (property names can't be inferred for arbitrary expressions) and your lambda expression isn't quite right either (the property names you use don't match the property names of the anonymous type). Try this instead:
var formsConfigItems = (from SPListItem i in list.GetItems(query)
select new
{
i.Title,
Value = Convert.ToString(i["Value"])
}).ToDictionary(a => a.Title, a => a.Value);
Ani's got the better solution IMO, but one other thing you're missing: Your LINQ statement is creating a collection of anonymous items, but you're not giving names to the properties in that anonymous class.
The k=>k.Key expression doesn't work, because it doesn't know what Key is - you've only defined Title (since you didn't give it a name, it borrowed the one from the object). The Value one can't be automatically figured out, so it would throw a compiler error.
To do it this way, you'd need to specifically declare the names:
new { Key = i.Title, Value = i["Value"].ToString() }

EF5 ObjectResult to ObjectQuery is null?

I'm trying to get the query string with parameters for debugging purposes from entity framework. No, I will not use EFProfiler because the query strings need to be visible as an output on the page. Most of those queries are written manually, using ExecuteStoredQuery(). However, casting them to ObjectQuery yields null.
Example:
ObjectResult<Parent> model = _context.ObjectContext().ExecuteStoreQuery<Parent>("SELECT * FROM Parents");
var objectQuery = model.AsQueryable() as ObjectQuery<Parent>;
objectQuery is null. ObjectContext() is a simple method in the datacontext that looks like:
return (this as IObjectContextAdapter).ObjectContext;
I have exhausted my own ideas and anything that is yielded back from searches is... well, useless, because nobody seems to get that problem. Mind, the results come back correctly from the query.
Edit: Right, I should have mentioned this, as well.
Doing this:
var oq = m as ObjectQuery<Parent>;
Yields me this:
Cannot convert type 'System.Data.Objects.ObjectResult' to 'System.Data.Objects.ObjectQuery' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion
Whence is when I thought it needed to be cast AsQueryable(), which, well... is null, for obvious reasons. It was my tired head that forgot to add this little detail.
I think the other anwers made clear that you can't cast an ObjectResult<T> to an ObjectQuery<T>. But I'm interested in your comment
I tried CreateQuery and I ran into problems with it. For one, it doesn't like when the user tries to SELECT *, you seem to have to specify all the fields you need to get.
You can use an ObjectQuery to do a "*" search:
ObjectContext().ObjectQuery<Parent>("SELECT VALUE par FROM Parents AS par")
As you see, there is no actual * in the query string and the trace string doesn't have a * but shows all fields. But you don't have to specify all field to build the query string. I hope this will help you to make better use of ObjectQuery.
Your cast as ObjectQuery<Parent> is not correct, cause ObjectResult<Parent>.AsQueryable() will return an IQueryable<Parent> that is not of type ObjectQuery<Parent>.
AsQueryable is the extension method of the class Queryable that converts an IEnumerable to an IQueryable.
The result is that your variable objectQuery is null.
Surely you ACTUALLY want to use CreateQuery(sql) rather than ExecuteStoreQuery(sql).
Given the name, ExecuteStoreQuery actually runs the SQL, whilst CreateQuery returns an ObjectQuery ready to be sent to the database.
Obviously you can then call Execute on ObjectQuery to return the ObjectResult

Do a linq query on an object array?

I have a collection of type objects which I know they are type Employee. I'd like to perform some linq operations on them using the employee some thing similar to this:
var options = (from e in m_Employees
select (e as Employee).DepartmentCode).Distinct();
But the as employee is not surprisingly giving an error. is there a way around it?
Changing the collection is not really an options since I,m maintaining the code and I want to avoid big changes.
You can use either
from e in m_Employees.Cast<Employee>()
select e.DepartmentCode
or
from e in m_Employees.OfType<Employee>()
select e.DepartmentCode
Cast thows an error if you can not cast each item to Employee, but OfType will filter out those objects not matching the type.
Rather than using as Employee it would be better to make the compiler basically insert a call to Cast<T> using an explicitly typed range variable:
var options = (from Employee e in m_Employees
select e.DepartmentCode).Distinct();
Or alternatively and equivalently:
var options = m_Employees.Cast<Employee>()
.Select(e => e.DepartmentCode)
.Disinct();
However, I still wouldn't have expected your original code to fail, if the array really does only include Employee references... If you were getting a NullReferenceException then either one of the values was null, or it was a non-null reference to a non-Employee object. These will both still give you an error with the above code, but you'll be able to see which one based on whether you still get a NullReferenceException or an InvalidCastException.
In general you should only use as when you're going to use the result conditionally. If you're sure that every value is really of the right type, you should use a cast instead - so that if you're wrong, you'll get the code blowing up with an exception instead of propagating a null reference to the rest of the code, where it could cause harm later on and make it hard to spot the source of the error.
If you were getting a compile-time error then there are a number of possible causes, based on what exception you were seeing.
EDIT: Okay, so it was an IEnumerable causing a compile-time error... Cast<T>() and OfType<T>() are both extension methods on just IEnumerable instead of on IEnumerable<T>.
One option would be:
(from e in m_Employees
let x = e as Employee
select x.DepartmentCode).Distinct();
Use this:
var options = (from e in m_Employees.Cast<Employee>()
select e.DepartmentCode).Distinct();
The important part is the Cast<Employee>. My answer assumes that m_Employees is an IEnumerable like ArrayList instead of an IEnumerable<Employee> like List<Employee>.
You could try this:
var options = m_Employees
.Cast<Employee>()
.Select(item => item.DepartmentCode)
.Distinct();

How to convert the following lines to Generic LINQ statements

I have the following lines of code in C#
that gets data using DataTables
This is pretty generic and helps me with multiple tables.
object obj = ((DataRowView)editingElement.DataContext).Row[this.SelectedValuePath];
I want to change this statement to a generic LINQ statement so that i can use it with multiple LINQ tables too.
Can somebody help me figure this out?
I don't think you can use LINQ to make the code you wrote nicer or more elegant in any way. I assume that the type of editingElement.DataContext is object, so you'll need to write the cast anyway. If you forget about the casting, your code is just indexed access:
var rows = (DataRowView)editingElement.DataContext;
object obj = rows.Row[this.SelectedValuePath];
LINQ doesn't have any features that would make indexing nicer, so I think this is the best you can get. One possible ugly thing is that you get object as the result and you'll need to cast that to some other type (e.g. CustomerInfo).
If you were using LINQ from the beginning (to populate the data for the DataContext), you could probably write something like this to access the customer:
var rows = (IEnumerable<CustomerInfo>)editingElement.DataContext;
CustomerInfo info = rows.Row[this.SelectedValuePath];
This would be a bit more elegant, because you'd need just a single cast. However, I think that your code is fine and LINQ cannot help you (in this piece of code).

LinqDataSource Select syntax

Why is it the syntax of the Select property in the LinqDataSource so different from Linq I would write inline in C#? I mean like:
new (Id As MyId, Name As MyName)
vs
new (MyId = Id, MyName = Name)
And the syntax diverges more when you start doing things like concatenation in the projection. I am using this with a Entity Data model as the provider, if that has anything to do with it.
I would have expected something called a LinqDataSource would simply allow you to supply a compiled Linq query and be done with it.
Also I could find no documentation on the syntax of what is expected for the Select property other than the most simple cases of aliasing the fields. The Linq Concat command doesn't work, and it was only a stroke of luck that I found a blog where someone figured out an alternative. So in the future when trying to do any other manipulations I pretty much can only take wild guesses in the dark.
I think it is because the as keyword has already a different meaning in the language. The chosen syntax resembles the syntax of default parameters (.net 4.0 following) and is pretty clear IMHO.
Note that this explicit syntax is only necessary when a property name for an anonymous type cannot be inferred or is ambigous.

Categories

Resources