Experts!
Got stuck with one (might be simple for someone) question.
I have an object:
public class DataItem {
public string Title { get; set; }
public string ID { get; set; }
public Dictionary<String, Object> Values { get; set; }
}
What i need to do is to build linq .Where dynamicaly from "Caml like" Xml. On Xml parsing i use switch for conditions to build linq predicates:
switch (Type) {
case CriteriaType.Contains:
break;
case CriteriaType.Eq:
break;
...
and using PredicateBuilder (from C# 5.0/4.0 in a Nutshell) to combine Expressions, but thing is i have no idea how to build expression predicate e.g. GreaterOrEqual.
Idea was to have function like:
public Expression<Func<DataItem, bool>> Geq<T>(String field, Object value)
Where DataItem is object we're going to query, T is Dictionary item value type, parameters field is key from DataItem.Values dictionary and value is value of this key.
But here is the catch:
Since we're operating with objects they should be converted to type
.
And since we're not sure that DataItem.Values collection has key
we need, expression should be build as linq equivalent of
p => p.Values.ContainsKey(field) && p.Values[field] >= value
If someone could help me with this, that would be just awesome.
Also i would be very appreciated for some nice tutorials & links with "easy to understand" examples.
Could you just add .Where clauses to the query like this?
// query
var query = from d in dataItems select d;
switch (expressionType) {
case CriteriaType.Contains:
query = query.Where(d => d.Values.ContainsKey(field));
break;
case CriteriaType.Eq:
query = query.Where(d => d.Values[field] >= value);
break;
Also, I'm not sure I understand what you mean with "Since we're operating with objects they should be converted to type", but maybe you could redefine your DataItem class to be generic:
public class DataItem<T> {
public string Title { get; set; }
public string ID { get; set; }
public Dictionary<string, T> Values { get; set; }
}
#Silverlay I think what you are looking for is Dynamic LINQ. This is a library provided by the LINQ team itself.
What you need to do is use string expressions instead as shown in this blog -
http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
Related
I have 3 classes mapped with Entity Framework
public class A
{
public string Name { get; set; }
}
public class B
{
public string Name { get; set; }
public A A { get; set; }
}
public class C
{
public string Name { get; set; }
}
I have this Linq To Entities Where Condition
return queryableOfB.Where(b => b.A.Name = instanceOfC.Name);
Because this is a repetitive method in my logic, I want to create a method as:
protected void GetFilter<B, TBProperty, TCProperty>(
IQueryable<B> queryofB, C cModel,
Expression<Func<B, TBProperty>> bExpression,
Expression<Func<C, TCProperty>> cExpression)
{
var bExpValue = cExpression.Compile()(cModel);
queryofB.Where(b => b.Property.EndsWith(bExpValue)); // How can I compare two expressions? but adding "for example" an .EndsWith to expression 1?
}
It's important not to pass the .EndsWith in the expression because the decision of using EndsWith, StartsWith, Contains or exact comparison must be done in the method.
Thank you in advance Gurus.
actually where method expect function as predicate. you can try with below one.
its doing nothing just calling the where Extension method. its better to use there where method inline. But telling u the way how can u do it.
static IEnumerable<T1> M<T1>(IEnumerable<T1> objEnumerable, Func<T1, bool> wherePredicate)
{
return objEnumerable.Where(predicate);
}
// calling like
var listOfBObjects = GetBObjectIEnumerable();
C instanceOfC = GetCClassObject();
M<B>(listOfBObjects, b => b.A.Name = instanceOfC.Name);
update
you can use the
M<B>(listOfBObjects, b => b.A.Name.Equals(instanceOfC.Name));
M<B>(listOfBObjects, b => b.A.Name.StartsWith(instanceOfC.Name));
M<B>(listOfBObjects, b => b.A.Name.Contains(instanceOfC.Name));
to improve the performance and all. yes you can use the IQueryable rather than IEnumerable
I want to convert Expression<Func<DTOItem, bool>> predicate to Expression<Func<Item, bool>> predicate with entity framework where DTOItem is mapped class of entity and Item(a table in database) is entityframe work class.
IDTOItemRepository itemRepo = new DTOItemRepository();
DTOItem itemObject = itemRepo.Single(q => q.ItemID == 1 && q.ItemType == 1);
where q => q.ItemID == 1 && q.ItemType == 1 is of type Expression<Func<DTOItem, bool>>.
Now i need to convert this expression to Expression<Func<Item, bool>>
You can't. You can do the opposite easily enough.
Let's take a look at another example.
I promise that if you give me an apple, I can tell you if it's still fresh. I know everything there is to know about every single kind of apple.
How can you use my knowledge to determine if any given piece of fruit is still fresh? You can't. I know about apples, but I don't know about other types of fruit. I can't make the guarantee that if you give me any kind of fruit I'll know if its fresh or not.
Now what if you were asked to determine if any type of Red Delicious apple is fresh? Can you use the information I can give you to provide an answer to that question in all cases? Sure you can. I can handle any kind of apple, limiting your questions to just one kind is no problem for me.
You have a function that knows how to take any DTOItem and return a boolean. You can't use that to take an Item at all and return a boolean, because what if that item isn't a DTOItem? Then what will you do? But if you have a method that can take any item at all and return a boolean then by all means you can "pretend" that it only accepts DTOItems. It can handle any item, limiting it's input to just DTOItems isn't a problem.
Use AutoMapper and it's Queryable Extensions portion, it will do exactly that for you (but not in a direct way). What it does is let you turn a IQueryable<Item> to a IQueyable<DtoItem> so you could use your Expression<Func<DTOItem, bool>> on it, then when you go to perform the query aginst the backing store AutoMapper will transform the Expression<Func<DTOItem, bool>> portion in to a Expression<Func<Item, bool>> portion to be passed on to the EF provider.
public static void Main(string[] args)
{
//This needs to be done once when your program starts up.
//You may need to do a more complicated "CreateMap" depending on how Item and DTOItem relate.
Mapper.CreateMap<Item, DTOItem>();
RunMyProgram();
}
public DTOItem GetItem()
{
using (var context = new MyDatabaseContext())
{
IQueryable<Item> itemQuery = context.Items;
return itemQuery.Project().To<DTOItem>()
.Single(q => q.ItemID == 1 && q.ItemType == 1);
}
//the above translates to the equivalent code
/*
return itemQuery.Where(q => q.itemid == 1 && q.itemtype == 1)
.Select(a => new ItemDTO {ItemID = a.itemid, ItemType = a.itemType, SomeType = a.sometype} )
.Single();
*/
}
public class DTOItem
{
public int ItemID { get; set; }
public int ItemType { get; set; }
public String SomeType {get; set;}
}
public class Item
{
public int itemid { get; set; }
public string itemtype { get; set; }
public String sometype {get; set;}
}
public class MyDatabaseContext: DbContext
{
public MyDatabaseContext(): base()
{
}
public DbSet<Item> Items{ get; set; }
}
I want to take some elements by checking them with my custom function.
I have Person table:
public class Person
{
public int Id { get; set; }
public DateTime BirthDay { get; set; }
public string Name { get; set; }
...
}
I should to use my GetAge() and other functions to filter Persons list.
My following code doesnt work:
public List<Person> FilterPersons(int ageFrom, int ageTo...etc..)
{
var all = Database.Persons.AsQueryable();
all = from item in all
where GetAge(item.BirthDay) > ageFrom
select item;
all = from item in all
where GetAge(item.BirthDay) < ageTo
select item;
// other operations
...
}
I think I can write so. In every step to do this:
List<Person> newList = new List<Person>();
foreach (var item in all)
{
var itemAge = Common.GetAge(item.BirthDay);
if (itemAge > AgeFrom)
{
newList.Add(item);
}
}
all = newList.List();
But this is not best way I think, because I should do filter by many criteries. It will work with low speed.
How can I use my functions in Linq query?
Edit:
I showed GetAge() function for example. I have many functions like that. I wanted to know how to use my function.
Well, you can't.
If you want to have criteria used in Where clause of your SQL query, you need to write them directly as a linq.Expression so that entity may parse it and transform it into SQL, not an external function.
Somthing like this works :
DateTime date = DateTime.Now.AddDays(ageFrom);
all = from item in all
where item.BirthDay > date
select item;
Query Expressions are built in to the C# compiler and as such, it only understands the expression that are built in to the compiler.
For example, when you use the where keyword, it converts that to a call to the Where<TSource>(this IQueryable<TSource> source, Func<TSource, bool> predicate) method.
This is true of Linq To Objects and Linq To SQL. What's more, with Linq To SQL, the compiler then has to convert the Query Expression to SQL, which has no way of knowing the definition of your GetAge method.
Or you can use this syntax:
DateTime date = DateTime.Now.AddDays(ageFrom);
all = item.Where(x => x.BirthDay > date).ToList();
Why not use a List<Person>.FindAll() method and pass in a method filter as the predicate?
You would use the method like this.
List<Person> filteredPersons = allPersons.FindAll(FilterPersons);
Below is the a sample method you would use as your filter.
bool FilterPersons(Person p)
{
if(//enter criteria here to determine if you want to select the person)
return true;
else
return false;
}
To do what you want this may be the code you need.
bool FilterPersons(Person p)
{
var itemAge = Common.GetAge(item.BirthDay);
if( itemAge > AgeFrom )
return true;
else
return false;
}
Assuming you can apply filters on the result:
You can apply normal filters ( in linq expressions ) and than apply your functions on the result. Of course, you need to refactor your methods.
Something like this :
var result= Users.Where(s=>s.Name).ToList();
result= MyFilter(result);
I'm pretty certain I know the answer is no but as a last ditch attempt I thought I'd ask the question here.
I'm using EF code first to query a table in the usual fashion
_context.Set<Foo>().Where(f => f.Bar == 999);
which creates the following expression (I've just written this so it might be wrong).
{SELECT
[Extent1].[Test] AS [Test],
[Extent1].[Test2] AS [Test2],
FROM [dbo].[Foo] AS [Extent1]
WHERE 19 = [Extent1].[Bar]}
Now, is it possible to manually modify this query to change the table name to, say, Foo10? (probably not)
Failing that, does anybody know of a way I can "late bind" the table name in code first?
You're probably wondering "Why the dirty hack?" As usual, this is a legacy issue with a database that's got some design issues and can't be changed.
Thanks in advance.
Ps. I'm aware that I could use Database.SqlQuery but would rather not.
Why don't you use TPT inheritance on your model?
Similar to #Krizz's answer, but you avoid using dynamic LINQ.
Using your comment:
if a particular parameter has a value of 1 look in Foo1 if its 2 look in Foo2 and so on
So, you could do this:
var query = ctx
.Foos
.OfMyType(value)
.Where(f => f.Bar == 999) // f.Bar is on the base/abstract entity.
.ToList();
Where OfMyType is a custom extension method on IQueryable<T>:
public static IQueryable<T> OfMyType<T>(this IQueryable<T> source, string value)
{
switch (value)
{
case "1":
return source.OfType<Foo1>();
case "2":
return source.OfType<Foo2>();
// etc, etc
}
}
Most (if not all) of the properties will be on the abstract "Foo" entity, and you create derived entities for each of the tables, which each have their own backing table.
That way, "consuming" code (e.g the ones making the queries), need not care about the different tables/Foo's, they simply pass the "magic value" to your repository (hopefully your using one), then you can silently switch to the table you want.
Would that work?
Assuming you have reasonable number of tables, I would add them all into model and create a common interface all classes will implement and then select the adequate model and use Dynamic Linq for querying.
I am not sure if this works, haven't checked it and haven't worked with "EF code-first", but this is something I would try:
Let's say your table(s) Foo have fields - Bar, Pub, X and let X be the one which the respective table depends on?
Then, I would define interface:
interface IFoo
{
int Bar { get; set; }
string Pub { get; set; }
int X { get; set; }
}
Then each table will have its class in model:
[Table("Foo1")]
class Foo1 : IFoo
{
public int Bar { get; set; }
public string Pub { get; set; }
public int X { get; set; }
}
[Table("Foo2")]
class Foo2 : IFoo
{
public int Bar { get; set; }
public string Pub { get; set; }
public int X { get; set; }
}
Then you could filter them like following:
IQueryable GetAdequateFoo(int X)
{
switch (X) // you could use reflection here to dynamically call the given Set<Foo#>()
{
case 1:
return _context.Set<Foo1>();
case 2:
return _context.Set<Foo2>();
default:
return null;
}
}
IFoo GetFooByBarAndX(int bar, int X)
{
IQueryable context = GetAdequateFoo(X);
return context.Where("it.Bar == #0", bar).Cast<IFoo>();
}
Here is how you create a new IQueryable with a new/modified expression (EF core 5.0 at the time of this writing).
var expression = query.Expression;
//modify your expression usually by building a new one or rebuilding using an ExpressionVisitor
var newQuery = query.Provider.CreateQuery(expression);
Note: I was searching for editing an Expression on an IQueryable and this is the question that came first, but the details then focus on a very specific use case and the more general question hasn't been answered...
I have a collection IEnumerable. In a LINQ query, preferably, I would like to select only the properties in this collection from type T, into an anonymous type, where T is a POCO business object.
Example:
My IEnumerable contains properties "Name", "Age".
My POCO is:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
}
I want to achieve the same effect as below, but without hard-coding the members of the anonymous type, and instead using my PropertyInfo collection.
IEnumerable<Person> peeps = GetPeople();
var names = from p in peeps
select new {Name = p.Name, Age = p.Age};
If I was using Entity Framework, I could use Entity SQL with a dynamically constructed string where clause, but then although not strictly hard-code, I'm still using string names of the properties.
Could I not perhaps dynamically construct an expression for the .Select projection method that determines which properties are included in the result object?
You can't do that. The compiler needs to know statically the type of the items in the enumeration, even if it's an anonymous type (the var keyword denotes implicit typing, not dynamic typing)
Why do you need to do that ? If you explain what your requirement is, we can probably suggest another way to do it