I have a data layer which abstracts away the underlying implementation (Entity Framework) by accepting & returning models that are defined elsewhere. I want to be able to pass in a Func<T, bool> predicate to one of the methods to allow additional clauses to be applied when querying the database.
Since the generic models know nothing of the underlying Entity Framework implementation, I need to convert my Func<T, bool> predicate to a predicate which can be applied against the Entity Framework context.
The generic models & Entity Framework models have exactly the same property names & I already have a class to perform the property value mappings between the two. Is there a way to convert the target type of the generic model delegate to that of the Entity Framework model?
An example of what I'm attempting to do:
Given this delegate: func = Func<Schema.Data.Order, bool>
I want to convert it to: dbFunc = Func<Models.Order, bool>
And apply it to the context: ctx.Orders.Where(dbDel)
I found this post, but I can't piece together how to perform the conversion. Been bumping my head against this the whole evening, so any help would be greatly appreciated!
UPDATE
The original question & requirements seem to have been a bit vague, so I'll elaborate on my implementation & my requirements. The following code samples have been amended to use Expression<Func<TIn, TOut>> & not Func<TIn, TOut>, based on the suggestions from hvd & Alexei.
I have an interface & a set of classes that represent my data layer. These act as a façade over the data source, allowing for different implementations in order to access the database. I want to be able to pass through additional filter criteria as a predicate & apply this to the underlying data source. But since the façade is separate from the underlying implementation, the predicate is defined using the façade model classes. Furthermore, the façade model class properties have the same naming as that of my implementation, so direct property assignments using reflection, for example, is possible.
My façade implementation:
namespace Schema.Data
{
public interface IDataStore
{
public IEnumerable<Order> GetOrders(string custCode, Expression<Func<Order, bool>> exp);
}
public class Order
{
public string CustomerCode { get; set; }
public string OrderNumber { get; set; }
}
}
I then implement the interface in a separate namespace, using Entity Framework to query my database:
namespace Data.EF
{
// Entity Framework model is in this same namespace
public class DataStore : Schema.Data.IDataStore
{
public IEnumerable<Schema.Data.Order> GetOrders(string custCode, Expression<Func<Schema.Data.Order, bool>> exp)
{
using (var ctx = new MyDatabaseEntities()) {
// TODO: Convert "exp" to Expression<Func<Data.EF.Order, bool>> to pass it in below
var orders = ctx.Orders.Where(e => e.CustomerCode == custCode).Where(dbExp ?? (n => true));
// handling the retrieved data & returning result goes here
}
}
}
}
If you just need to convert function taking one type to function taking another type use regular composition approach -
TDest MapSourceToDest(TSource v) {....}
Func<TSource, bool> func = ...;
Func<TDest, bool> dbFunc = x => func(MapSourceToDest(x));
Mapping can be done by hand, reflection, libraries or many other approaches - How to map properties of two different objects?.
Note that if you actually need to pass such method to EF you need Expression and not Func - in this case it actually possible to rewrite expression with destination type - How to change a type in an expression tree?
Related
I currently use EF Core 3.0.
So, I want to realize TPH model data selection through the base table requesting.
Let's see the example:
public class BaseClass
{
public int Base {get;set;}
}
public class Foo : BaseClass
{
public int FooMember {get;set;}
}
public class Bar : BaseClass
{
public int BarMember {get;set;}
}
public DbSet<BaseClass> dbSet {get;set;}
And I want to implement code like this:
var getInheritedSet = dbSet.OfType(typeIStronglyNeed);
But I can only do something like this:
var getInheritedSet1 = dbSet.OfType<Foo>;
var getInheritedSet2 = dbSet.OfType<Bar>;
Could you explain why EF Core 3.0 has no OfType(Type type) but only OfType<TType>()?
And the second question - how is it possible to get inherited data types from DbSet?
Thank you.
The generic method OfType<T> is a standard LINQ method which EF Core supports.
There is no standard OfType(Type) method and EF Core designers didn't find a reason of implementing such custom EF Core specific extension.
But it's not hard to be implemented. TPH (and other future database inheritance strategies) are supported in LINQ to Entities queries by is, as and cast operators. So what you need is the equivalent of
Where((BaseClass e) => e is some_type)
Such expression cannot be created at compile time, but can easily be created using the Expression class methods (in particular Expression.TypeIs) like this:
public static IQueryable<T> OfType<T>(this IQueryable<T> source, Type type)
{
var parameter = Expression.Parameter(typeof(T), "e");
var body = Expression.TypeIs(parameter, type);
var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
return source.Where(predicate);
}
And the second question - how is it possible to get inherited data types from DbSet?
EF Core metadata is exposed through DbContext.Model property. You can use FindEntityType to get the IEntityType describing the entity type
var entityType = dbContext.Model.FindEntityType(typeof(BaseClass));
and now there are a lot of available methods regarding inheritance like GetDerivedTypes, GetDerivedTypesInclusive, GetDirectlyDerivedTypes, GetConcreteDerivedTypesInclusive etc. The last one can be used to retrieve the whole TPH hierarchy excluding the abstract types. And also GetDiscriminatorProperty and GetDiscriminatorValue to get the discriminator column name, type and value for each entity in the TPH. For instance
var discriminatorProperty = entityType.GetDiscriminatorProperty();
var typeInfo = entityType
.GetConcreteDerivedTypesInclusive()
.Select(t => new
{
Type = t,
DiscriminatorValue = t.GetDiscriminatorValue()
})
.ToList();
I am currently using one of the many repository patterns available online to perform CRUD operations with EF6. I am happy to use it as is but I have recently been handed a few legacy projects that have database tables with a very high number of columns. I would like a way to make my application as well as future applications smoother by devising a way to select only a subset of columns.
Current method.
public virtual TEntity Get(Expression<Func<TEntity, bool>> where,
params Expression<Func<TEntity, object>>[] navigationProperties)
{
TEntity item = null;
IQueryable<TEntity> dbQuery = this.Context.Set<TEntity>();
//Apply eager loading
foreach (Expression<Func<TEntity, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Include<TEntity, object>(navigationProperty);
item = dbQuery
.AsNoTracking() //Don't track any changes for the selected item
.FirstOrDefault(where); //Apply where clause
return item;
}
I would like to enhance that method to retrieve only the columns I require but still return TEntity.
I do know I have to inject a Select after the '.AsNoTracking()' but I am unsure as to how I could pass the properties in as I am only starting out with Expression Trees.
In essence I would like to be able to do this.
public class Employee
{
public int EmployeeId { get;set; }
public string EmployeeRole { get;set; }
public string EmployeeFirstName { get;set; }
public string EmployeeLastName { get;set; }
public string DOB { get;set; }
...
}
Employee employee = EmployeeRepository.Get(where: e => e.EmployeeRole == "Developer",
columns: x => x.EmployeeFirstName, x => x.EmployeeLastName,
navigationProperties: null);
Where columns is a list of expressions specifying the columns to be added to the Select clause.
Any help would be appreciated.
Thanks in advance...
Update.
I ended up with using a DTO to do the necessary querying and extraction as I couldn't find an elegant way to perform it generically. There was a solution developed by a colleague of mine but it made the repository far too complex and would have been hard to manage in the future.
So I create a StaffBasicInformation class to hold the subset of columns I use regularly. I also created an interface for it if I needed in the future. The below code sample shows the final implementation of retrieving data for the DTO.
public virtual IStaffBasicInformation GetStaffBasicInformation<TEntity2>(Expression<Func<TEntity2, bool>> where)
where TEntity2 : ActiveStaffMember
{
TEntity2 item = null;
StaffBasicInformation resultItem = null;
IQueryable<TEntity2> dbQuery = this.Context.Set<TEntity2>();
resultItem =
dbQuery.Where(where)
.Select(x => new StaffBasicInformation
{
GivenName = x.GivenName,
Department = x.Department,
Description = x.Description,
DisplayName = x.DisplayName,
Gender = x.Gender,
IID = x.IID,
Mail = x.Mail,
Title = x.Title,
ID = x.Id
})
.FirstOrDefault();
return resultItem;
}
Your return value will not be of type TEntity anymore after you have done the projection, it will be an anonymous type. You have to decide, if you want to map this anonymous type to an instance of TEntity, including mapping all navigationproperties, or return dynamic or object from your repository. Both choices are not very pleasant. Mapping would include a huge amount of reflection, which will not be very fast. By returning a dynamic type you loose all type safety. You have seen this problem allready i assume.
That said: You will need to build this expression manually. Based on this answer you can modify the
public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
to
public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<Expression> fieldNames)
end extract the property names from the expressions. I would suggest to use an ExpressionVisitor for that, but you could also use the code from this answer
For the mapping you can compile the expressions and use the returned Func to retrieve the value from the anonymous type. After that you would need to use expression and find the hosting type for the selected property by using an ExpressionVisitor again. Then you will need to create a object of type of TEntity via Activator.CreateType(), and objects for every hosting type. Assign the value from Func(AnonymousType) to the created object of the hosting type using the property name from the expression. After that you have to determin the relationship between TEntity and the hosting type and build it up.
I will try to post some code tomorrow for this scenario, although i am quite sure there is a better and faster way.
Im using Entity Framework 4, I have an entity
public partial class UserEntity : EntityObject
{
public string id_user;
public string pass;
public DateTime date_added;
}
and a data transfer object with custom attributes
[DataTransferObject("UserEntity")]
public class User
{
[EntityColumn("id_user")]
public string idUser { get; set; }
[EntityColumn("pass")]
public string password { get; set; }
[EntityColumn("date_added")]
public string dateAdded { get; set; }
}
I convert the entity to dto and the dto to entity using these attributes with reflection, I'm using a generic repository for data access methods, when I want to make a query, using Enumerable.Where, to the ObjectSet of the entity, this require a Func<dynamic, bool> (dynamic because the program doesn't know the entity implementing the repository), this works but I want a Func<DTObject, bool> as parameter so I don't have to use entities in business layer, is there a way to achieve something like this?
public static Func<dynamic, bool> ConvertFunc<DTObject>(Func<DTObject, bool> predicate)
{
/* do some magic to convert func */
return newFunc;
}
Func<dynamic, bool> newFunc = ConvertFunc<User>(u => u.idUser == "admin" && u.password == "abc123");
/* newFunc = u => u.id_user == "admin" && u.pass == "abc123" */
I need to stop you right here. You are completely wrong on your approach. You need to use Queryable.Where<T>(this IQueryable<T> query, Expression<Func<T, bool>>) for EntityFramework to work. The method you would actually be using is Enumerable.Where<T>(this IEnumerable<T> set, Func<T, bool> predicate), which would do the filtering IN MEMORY. This means the road you are going down will download your entire table into memory.
For this to work you need to be playing with the System.Linq.Expressions namespace. However, if this is going to work, without some insane amount of logic (more than already exists with Expressions). Your Dto signature will be exactly the same as your EntityObject.
Given this is true, what value does your DTO add (given the maintenance cost of the class)?
The only value I see is the decoupling of your domain logic from your data storage (ie you don't have EntityObject in your domain layer). However that problem was solved years ago with POCOs.
TLDR
XY problem. Use POCO instead.
I would like to pass an IQueryable and an array of ids to a method which filters the IQueryable based on those ids.
As the ids can be either long's or int's it should be solved generically.
I came up with the following:
public static IEnumerable<T> GetModified<TId, T>(IQueryable<T> objects, TId[] ids) where T : class
{
return objects.Where(j => ids.Contains((TId)j.GetType().GetProperty("Id").GetValue(j)));
}
Unfortunately I'm getting the exception:
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
The exception is normal, as getting properties through reflection is something that clearly cannot be translated to SQL.
One thing I would try is to create a generic interface that exposes an Id property of a given type:
public interface HasId<T> {
T Id { get; set; }
}
Now you could declare your entity as implementing HasId<int>, for example, if the Id was of type int.
The next step is to modify your method like so:
public static IEnumerable<T> GetModified<TId, T>
(IQueryable<T> objects, TId[] ids) where T : class, HasId<TId>
{
return objects.Where(j => ids.Contains(j.Id));
}
Note the added generic restriction: where T : class, HasId<TId>. This enables you to write the simplified j.Id, which returns a TId value, instead of resorting to reflection.
Please note that I haven't run or tested this code; it's just an idea that I got when I saw your problem and I hope it helps.
Update:
Here's another possible solution that doesn't require that you declare interfaces or change your classes in any way:
public static IEnumerable<T> GetModified<TId, T>
(IQueryable<T> objects, TId[] ids, Expression<Func<T, TId>> idSelector)
where T : class
{
return objects.Where(j => ids.Contains(idSelector(j)));
}
What I've done here is add the Expression<Func<T, TId>> idSelector parameter, an expression that can return the Id of a given instance of T.
You would call the method like that:
var modified = GetModified(dbObjects, yourIdArray, entity => entity.Id);
(only the third parameter being new; keep the others as you have them now).
Again, I haven't tested if this works or even compiles, as I don't have a computer with VS here :(.
Entity Framework doesn't support some of the .NET methods such as GetValue() since it does not translate to SQL (which is the code actually executed to the IQueryable. Try calling ToList to get the CLR object before doing reflection:
public static IEnumerable<T> GetModified<TId, T>(IQueryable<T> objects, TId[] ids) where T : class
{
return objects.ToList().Where(j => ids.Contains((TId)j.GetType().GetProperty("Id").GetValue(j)));
}
I'm trying to create a generic repository for my models. Currently i've 3 different models which have no relationship between them. (Contacts, Notes, Reminders).
class Repository<T> where T:class
{
public IQueryable<T> SearchExact(string keyword)
{
//Is there a way i can make the below line generic
//return db.ContactModels.Where(i => i.Name == keyword)
//I also tried db.GetTable<T>().Where(i => i.Name == keyword)
//But the variable i doesn't have the Name property since it would know it only in the runtime
//db also has a method ITable GetTable(Type modelType) but don't think if that would help me
}
}
In MainViewModel, I call the Search method like this:
Repository<ContactModel> _contactRepository = new Repository<ContactModel>();
public void Search(string keyword)
{
var filteredList = _contactRepository.SearchExact(keyword).ToList();
}
Solution:
Finally went with Ray's Dynamic Expression solution:
public IQueryable<TModel> SearchExact(string searchKeyword, string columnName)
{
ParameterExpression param = Expression.Parameter(typeof(TModel), "i");
Expression left = Expression.Property(param, typeof(TModel).GetProperty(columnName));
Expression right = Expression.Constant(searchKeyword);
Expression expr = Expression.Equal(left, right);
}
query = db.GetTable<TModel>().Where(Expression.Lambda<Func<TModel, bool>>(expr, param));
Interface solution
If you can add an interface to your object you can use that. For example you could define:
public interface IName
{
string Name { get; }
}
Then your repository could be declared as:
class Repository<T> where T:class, IName
{
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => i.Name == keyword);
}
}
Alternate interface solution
Alternatively you could put the "where" on your SearchExact method by using a second generic parameter:
class Repository<T> where T:class
{
public IQueryable<T> SearchExact<U>(string keyword) where U: T,IName
{
return db.GetTable<U>().Where(i => i.Name == keyword);
}
}
This allows the Repository class to be used with objects that don't implement IName, whereas the SearchExact method can only be used with objects that implement IName.
Reflection solution
If you can't add an IName-like interface to your objects, you can use reflection instead:
class Repository<T> where T:class
{
static PropertyInfo _nameProperty = typeof(T).GetProperty("Name");
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => (string)_nameProperty.GetValue(i) == keyword);
}
}
This is slower than using an interface, but sometimes it is the only way.
More notes on interface solution and why you might use it
In your comment you mention that you can't use an interface but don't explain why. You say "Nothing in common is present in the three models. So i think making an interface out of them is not possible." From your question I understood that all three models have a "Name" property. In that case, it is possible to implement an interface on all three. Just implement the interface as shown and ", IName" to each of your three class definitions. This will give you the best performance for both local queries and SQL generation.
Even if the properties in question are not all called "Name", you can still use the nterface solution by adding a "Name" property to each and having its getter and setter access the other property.
Expression solution
If the IName solution won't work and you need the SQL conversion to work, you can do this by building your LINQ query using Expressions. This more work and is significantly less efficient for local use but will convert to SQL well. The code would be something like this:
class Repository<T> where T:Class
{
public IQueryable<T> SearchExact(string keyword,
Expression<Func<T,string>> getNameExpression)
{
var param = Expression.Parameter(typeof(T), "i");
return db.GetTable<T>().Where(
Expression.Lambda<Func<T,bool>>(
Expression.Equal(
Expression.Invoke(
Expression.Constant(getNameExpression),
param),
Expression.Constant(keyword),
param));
}
}
and it would be called thusly:
repository.SearchExact("Text To Find", i => i.Name)
Ray's method is quite good, and if you have the ability to add an interface definitely the superior however if for some reason you are unable to add an interface to these classes (Part of a class library you can't edit or something) then you could also consider passing a Func in which could tell it how to get the name.
EG:
class Repository<T>
{
public IQueryable<T> SearchExact(string keyword, Func<T, string> getSearchField)
{
return db.GetTable<T>().Where(i => getSearchField(i) == keyword);
}
}
You'd then have to call it as:
var filteredList = _contactRepository.SearchExact(keyword, cr => cr.Name).ToList();
Other than these two options you could always look into using reflection to access the Name property without any interface, but this has the downside that there's no compile-time check that makes sure the classes you're passing actually DO have a Name property and also has the side-effect that the LINQ will not be translated to SQL and the filtering will happen in .NET (Meaning the SQL server could get hit more than is needed).
You could also use a Dynamic LINQ query to achieve this SQL-side effect, but it has the same non type-safe issues listed above.