Storing LINQ queries as variables/constants? - c#

Say I have a LINQ-to-XML query that generates an anonymous type like this:
var aQuery =
(from a in document.Root.Elements("items")
select new {
id = a.Attribute("id").Value,
type = a.Attribute("type").Value,
modified = a.Attribute("modified").Value
});
if there a way to store that query expression in a variable or constant and then execute at runtime? The basic idea is that I have a bunch of these expressions and it would be handy if they could all be defined in one place and then invoked dynamically thru a single method where I just need to pass in the XML document and which expression to use. Thanks.

You could define them as methods quite easily, though you'd forfit the right to use anonymous types.
public static IQueryable<Item> GetItemsFromXml(XDocument document)
{
return (from a in document.Root.Elements("items")
select new Item
{
Id = a.Attribute("id").Value,
Type = a.Attribute("type").Value,
Modified = a.Attribute("modified").Value
});
}
Having said that, patterns like the repository pattern are used to wrap the whole process of accessing data.

Related

How to construct IEnumerable from a list of lambda expression in c#?

In a C# project, I want to be able to create a function that accepts an IQuereable<User> object along with multiple lambda's expressions then converts it into a different object. However, instead of pulling all the available properties from the database, I want to pull only the properties that are provided to the function. Here is the logic that I think I need to follow
Figure out what properties that I need to select
Construct an IEnumerable from the IQueryable by specifying which properties that are needed.
Iterate over every property that was selected and create the Student object.
In other words, if I call .ToList() on the IQuereable<User> users object, the query will select * from the databse table which pull all available columns. Instead I want to select only the properties that are passed as labda expressions.
Here is my code
public IEnumerable<Student> Make(IQuereable<User> users, Expression<Func<User, dynamic>> keyProperty, params Expression<Func<User, dynamic>>[] propertiesToSelect)
{
var students = new List<Student>();
// Make a distinct list of lambda's expressions that I need to select
var props = propertiesToSelect.ToList();
props.Add(keyProperty);
props = props.Distinct().ToList();
// TO DO!!! Some how, I need to only select the properties that are in **props** array insted of pulling all available properties
var selectableUsers = users.Select(/**/).ToList();
foreach(var user in selectableUsers)
{
var student = new Student();
foreach(Expression<Func<User, object> exp in props)
{
var prop = GetPropertyInfo(user, exp)
object value = prop.GetValue(user, null);
// Set the property student property
// Do somthing with prop.Name...
// Do something with value...
}
students.Add(student);
}
return strudents;
}
Question How can I use LINQ to select only the list of Expressions
This may be a case for using a stored procedure and dynamic SQL to build your initial query and control the contents of the SELECT statement.
The other option I see is to define a DTO object and leverage the AutoMapper Queryable Extensions and use the Explicit Expansion functionality to control the data to return.

Is there any way of knowing the content of an IQueryable object without using Reflection?

I have to work with a given class "QueryGenerator" that generates dynamic queries from selected Tables and Columns by the user using a StringConnection and a Provider. Anyways, i don't have to know the implementation of the class but i have to use it and i'm stock.
At the end, the "QueryGenerator" returns the result query as an object, the only thing that i know (because i use Reflector on the class) is that i can do an IQueryable cast on that query result. Here is an example:
var result = (IQueryable)myQueryGenerator.Result;
And for knowing the content of result, i have to use Reflection.
So, is there any better way of finding out the content of result , and, for example, fill a DataSet with it?
No, there isn't.
IQueryable could return objects of different types (e.g. if you query against an array of objects). In this extreme case you would need to determine the type for each individual item in the enumerated query as you access them.
Example code to demonstrate this scenario:
object[] objs = new object[3]{ "string", 78, DateTime.Now };
var q = objs.AsQueryable().Skip(1).Take(2);
foreach( var o in q )
{
var t = o.GetType();
}

How to define anonymous method types to build dynamic queries with LINQ?

I'm busy with a LINQ to SQL project that basically creates multiple threads for each entity type in my database, which constantly queries information from the DB in a thread.
Here's a pseudo example:
streamer.DefineDataExpression<Contacts>(x => x.FirstName == "Bob");
while(true)
{
List<Contacts> MyContactsResult = streamer.ResultList;
// do whatever with MyContactsResult
}
The above code doesn't exist, but this is what I have so far for the 'streamer' class (it obviously doesn't work, but you can see what I'm trying to achieve above):
public void DefineExpression(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
using (var db = new LINQDataContext())
{
ResultList = db.GetTable<T>().Where(expression);
}
}
How do I go about creating a method like 'DefineExpression' that will allow me to query a LINQ type dynamically?
Why not use the Dynamic LINQ provider, as mentioned by Scott Guthrie. I think that would give you everything you are looking for, because you can define the query as a string. Therefore, you can more easily build a string representation of your query, and execute on the fly.

How to create a custom property in a Linq-to-SQL entity class?

I have two tables Studies and Series. Series are FK'd back to Studies so one Study contains a variable number of Series.
Each Series item has a Deleted column indicating it has been logically deleted from the database.
I am trying to implement a Deleted property in the Study class that returns true only if all the contained Series are deleted.
I am using O/R Designer generated classes, so I added the following to the user modifiable partial class for the Study type:
public bool Deleted
{
get
{
var nonDeletedSeries = from s in Series
where !s.Deleted
select s;
return nonDeletedSeries.Count() == 0;
}
set
{
foreach (var series in Series)
{
series.Deleted = value;
}
}
}
This gives an exception "The member 'PiccoloDatabase.Study.Deleted' has no supported translation to SQL." when this simple query is executed that invokes get:
IQueryable<Study> dataQuery = dbCtxt.Studies;
dataQuery = dataQuery.Where((s) => !s.Deleted);
foreach (var study in dataQuery)
{
...
}
Based on this http://www.foliotek.com/devblog/using-custom-properties-inside-linq-to-sql-queries/, I tried the following approach:
static Expression<Func<Study, bool>> DeletedExpr = t => false;
public bool Deleted
{
get
{
var nameFunc = DeletedExpr.Compile();
return nameFunc(this);
}
set
{ ... same as before
}
}
I get the same exception when a query is run that there is no supported translation to SQL. (
The logic of the lambda expression is irrelevant yet - just trying to get past the exception.)
Am I missing some fundamental property or something to allow translation to SQL? I've read most of the posts on SO about this exception, but nothing seems to fit my case exactly.
I believe the point of LINQ-to-SQL is that your entities are mapped for you and must have correlations in the database. It appears that you are trying to mix the LINQ-to-Objects and LINQ-to-SQL.
If the Series table has a Deleted field in the database, and the Study table does not but you would like to translate logical Study.Deleted into SQL, then extension would be a way to go.
public static class StudyExtensions
{
public static IQueryable<study> AllDeleted(this IQueryable<study> studies)
{
return studies.Where(study => !study.series.Any(series => !series.deleted));
}
}
class Program
{
public static void Main()
{
DBDataContext db = new DBDataContext();
db.Log = Console.Out;
var deletedStudies =
from study in db.studies.AllDeleted()
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
}
}
This maps your "deleted study" expression into SQL:
SELECT t0.study_id, t0.name
FROM study AS t0
WHERE NOT EXISTS(
SELECT NULL AS EMPTY
FROM series AS t1
WHERE (NOT (t1.deleted = 1)) AND (t1.fk_study_id = t0.study_id)
)
Alternatively you could build actual expressions and inject them into your query, but that is an overkill.
If however, neither Series nor Study has the Deleted field in the database, but only in memory, then you need to first convert your query to IEnumerable and only then access the Deleted property. However doing so would transfer records into memory before applying the predicate and could potentially be expensive. I.e.
var deletedStudies =
from study in db.studies.ToList()
where study.Deleted
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
When you make your query, you will want to use the statically defined Expression, not the property.
Effectively, instead of:
dataQuery = dataQuery.Where((s) => !s.Deleted);
Whenever you are making a Linq to SQL query, you will instead want to use:
dataQuery = dataQuery.Where(DeletedExpr);
Note that this will require that you can see DeletedExpr from dataQuery, so you will either need to move it out of your class, or expose it (i.e. make it public, in which case you would access it via the class definition: Series.DeletedExpr).
Also, an Expression is limited in that it cannot have a function body. So, DeletedExpr might look something like:
public static Expression<Func<Study, bool>> DeletedExpr = s => s.Series.Any(se => se.Deleted);
The property is added simply for convenience, so that you can also use it as a part of your code objects without needing to duplicate the code, i.e.
var s = new Study();
if (s.Deleted)
...

Can this Linq query be typed as anything other than "var"?

When I do a query that returns an anonymous type
var assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new {p.Asset, p.Asset.PropertyTbl};
Can I type the return to anything other than var?
You cannot* return an anonymous type because the caller would not know what type it is and wouldn't be able to use it.
If you want to return the results, you can create objects of a non-anonymous type:
IEnumerable<Foo> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new Foo { Bar = p.Asset, Baz = p.Asset.PropertyTbl};
You can also use the Tuple type in .NET 4 if you don't want to create a custom class for your values.
* This is not strictly true - it is possible but you should avoid doing it. Here is a link anyway if you really want to.
You can use object or dynamic (in .NET 4.0) instead of var but don't expect to find a name to an anonymous type. In your case using var is better as it will preserve the strong typing at least until you leave the scope of the current method.
You could define a new class:
public class AssetProp
{
public virtual string Asset {get;set;}
public virtual string PropertyTbl {get;set;}
}
And then you can return it as that class:
IEnumerable<AssetProp> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new AssetProp {p.Asset, p.Asset.PropertyTbl};
Not really, since the new {p.Asset, p.Asset.PropertyTbl} code creates an anonymous type. Even using object doesn't really gain you much since you can't cast it to anything useful later on, so you would have to use reflection to access the properties.
Not really. If you cast to object you wont be able to access the properties of your anonymous class.
The var keyword was specifically introduced for dealing with anonymous classes - why would you want to avoid it? If you need to return the data you should name the class.
You can if you use lambda expressions, otherwise you can do a cast but do some good exception handling.
you can also do this (it does relate much to your problem though, because you just move "var" somewhere else, but it's interesting that it recognize those types as same)
var element = new { id = 7 };
List<object> collection = new List<object>();
element = collection.Select(item => new { id = 0 }).First();

Categories

Resources