Is it possible to modify an IQueryable expression manually - c#

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...

Related

Using the logic from one lambda within a second lambda

I've looked at the other questions around this and I just can't work out how to apply the answers to my particular situation. Say you have a couple of models that look like this:
public class Person
{
public int PersonId { get; set; }
}
public class Business
{
public int BusinessId { get; set; }
}
I want to be able to write a couple of different generic methods: one that gets the models using a provided Lambda that might look something like this:
GetWhere(p => p.PersonId == 1)
And one to get the models using a unique key - to make this flexible, I'd like to be able to specify the unique key using a Lambda:
GetByUniqueKey(p => p.PersonId, 1)
Or
GetByUniqueKey(b => b.BusinessId, 1)
Ideally GetByUniqueKey would just be a shorthand method to build up an expression to send to GetWhere, and then return the FirstOrDefault() result. But the logic to do this is completely escaping me. What I want to do:
public IEnumerable<TModel> GetWhere(Expression<Func<TModel, bool>> whereExpression)
{
// Get from DB using expression provided
}
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeyProperty,
TUniqueKey value)
{
return GetWhere(m => uniqueKeyProperty(m) == value).FirstOrDefault();
}
So I want to take the uniqueKeyProperty expression, invoke it on the supplied parameter somehow to get the property, and then use that property in the whereExpression expression.
A note on duplicate questions:
I know this looks like a duplicate of other similar questions, but please note I have read those and I just can't figure out how to apply those answers to my specific use case.
Some clarification in response to comments:
Put simply, I want to do the following:
I want to take the Expression p => p.PersonId and the value 1, and generate a whereExpression that looks like this p => p.PersonId == 1. (Thanks #Rob)
You can build a new expression from the key selector and value provided like so:
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeySelector,
TUniqueKey value)
{
return GetWhere(Expression.Lambda<Func<TModel,bool>>(
Expression.MakeBinary(
ExpressionType.Equal,
uniqueKeySelector.Body,
Expression.Constant(value, typeof(TUniqueKey))),
uniqueKeySelector.Parameters));
}
For querying by ID I wouldn't bother with this approach. Check out the other static methods on the Expression class.
As apparently you would like to implement some kind of polymorphism, one possibility would be to have Person and Business inherit from the same base class or inherit from the same interface and share an Id property. You could define
public class Identifiable
{
public int Id { get; set; }
}
and make Person and Business inherit from in. Then the callback
Func<Identifiable,bool> = iIdentifiable => iIdentifiable.Id == 1
could be called for objects of both classes. However, the original classes would have to be changed for that approach to work.

Dynamicaly build linq to objects where expression

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

Linq Expressions: Create a max query using a generic DbSet

In order to simplify creating fake data for unit testing, I want to have a function that can create a generic Entity (which I use as an underlying object for a lot of other classes). The entity has an index, and, for generating a new index, I want to simply find the currently highest number and add one.
I am using Index for a lot of other classes, and would like to make this function as generic as possible. My problem is that I don't know how to specify what DbSet to use in my generic function GetMaxID.
This is what I got so far:
private Entity CreateGenericEntity()
{
return new Entity()
{
Guid = Guid.NewGuid(),
Index = GetMaxID<Entity>(x => x.Index) + 1,
};
}
private int GetMaxID<TEntity>(Expression<Func<TEntity, int>> expression)
{
return _repository.Set<TEntity>().Max(expression);
}
_repository has a bunch of different IDbSets properties, such as
public IDbSet<Customers> Customers{ get; set; }
public IDbSet<Orders> Orders{ get; set; }
etc.
I found that I was missing the declaration of TEntity, which was fixed by appending where TEntity : class to my function. I also had to change the expression to accept int? in order to handle the case where the query returns a null value. The complete function looks like this (if anyone is interested)
private int GetMaxID<TEntity>(Expression<Func<TEntity, int?>> expression) where TEntity : class
{
return _repository.Set<TEntity>().Max(expression) ?? 0;
}

Relate two lists with LINQ extensions

I have two lists of different objects, one from a third party API and one from my database - and I'm trying to link the two as a relationship. Ideally with a similar effect of how DBML's create relationships for tables with foreign keys (Customer.Orders).
From third party:
class ApiObject {
public string ID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
... 30 other properties ...
}
From my database:
class DbmlObject {
public int ID { get; set; }
public string ApiID { get; set; }
public string OtherString { get; set; }
}
They are related through ApiObject.ID == DbmlObject.ApiID
I do not want to merge these, nor join them into some anonymous object (and explicitly list 30+ properties) - but rather to make the DbmlObject a linked property of ApiObject. i.e.: addressable as:
apiObject.DbmlObjects.First().OtherString or ideally apiObject.DbmlObject.OtherString since it is a 1 to 1 relationship.
In controller:
List<ApiObject> apiObjects = _thirdParty.GetObjects();
DbmlDataContext model = new DbmlDataContext();
List<DbmlObject> dbmlObjects = model.GetAllDbmlObjects();
// relate them here
foreach (var apiObject in apiObjects)
Console.Write(apiObject.DbmlObject.OtherString)
// NOTE: ideally this foreach loop should not make a DBML query on each iteration, just the single GetAllDbmlObjects query above.
It sounds like a join:
var combined = from api in apiObjects
join dbml in dbmlObjects on api.ID equals dbml.ApiID
select new { api, dbml }
In order to get DbmlObject "in" the ApiObject, you will need to either inherit ApiObject and construct a new one of that class, which includes the Dbml property, or create a entirely new class to return. If you need static typing this is the best you can do - of course you could (mis)use dynamic to get what you want.
In this case, you are mentioning (in comments) that the ApiObject class is from a third party library that you can't change - in this case I would probably choose to create a new type which takes an instance of both objects in the constructor and exposes the properties you need - a decorator. Yes, it looks like a lot of code, but it is not complex, good tools will autogenerate it for you - and you get the class that you need for your code to be succinct.
In case you want to go further with returning an IEnumerable<dynamic>, you could build a "combining dynamic" object based on DynamicObject that then responds to all the properties of ApiObject and DbmlObject - or just adds DbmlObject as a property. I am not saying this is the right way to go, it depends on what you need it for - remember you are losing type safety. Here is a simple example:
void Main()
{
dynamic dyn = new CombiningDynamic(new Foo { X = 3 }, new Bar { Y = 42 });
Console.WriteLine(dyn.X);
Console.WriteLine(dyn.Y);
}
public class Foo
{
public int X {get;set;}
}
public class Bar
{
public int Y { get;set;}
}
public class CombiningDynamic : DynamicObject
{
private object [] innerObjects;
public CombiningDynamic(params object [] innerObjects)
{
this.innerObjects = innerObjects;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach(var instance in innerObjects)
{
Type t = instance.GetType();
PropertyInfo prop = t.GetProperty(binder.Name);
if (prop != null && prop.CanRead)
{
result = prop.GetValue(instance, null);
return true;
}
}
result = null;
return false;
}
}
Remember, this is example code. If you really go this way, you would want to perhaps override some more of the methods (TrySetMember, ...), and you most definetely would want to cache the reflection results so you don't need to walk the types each time - reflection is (comparatively) slow.

Create LINQ query at runtime to GroupBy in EntityFramework (with inheritance)

This is the scenario. I have the following three classes, they are defined in Entity Framework, i only define them here for the example:
public class Foo
{
public string Color { get; set; }
}
public class Bar : Foo
{
public string Height { get; set; }
}
public class Pipe : Foo
{
public string Width { get; set; }
}
So, I have many Foo's, this is my base class, I want to be able to specify a propery, and do this query:
from e in Context.Foos
group e.Color by e.Color into result
select new
{
Value = result.Key,
ValueCount = result.Count()
}
This should end up with:
Blue 2
Black 4
Yellow 2
This works, however I want to specify this at run time, with the Property name 'Color' passed by the client. Also, I want to search the derived entities too. If i try to do
group e.Height by e.Height into result
It wont work because there is no Height in Foo, only in Bar. But the point is I ONLY want to return Bars, this should also be specified at runtime. This is the main problem I have been having. I cant do Foos.OfType<Bar>.GroupBy(some dynamic stuff) because I dont know the type to filter for at runtime.
Would really appreciate some help on this matter.
EDIT
Basically, what i'm trying to do is this System.LINQ.Dynamic: Select(" new (...)") into a List<T> (or any other enumerable collection of <T>) but return Count instead of Sum at the end.
In this answer, a func is being used to create a dynamic Where.
private List<T> GetResults<T>(IQueryable<T> source,
Expression<Func<T, bool>> queryFunction)
{
return source.Where(queryFunction).ToList<T>();
}
You should be able to do something similar with GroupBy.

Categories

Resources