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.
Related
I have a repository class which takes QueryConstraints object as a parameter to query the data store and return an IEnumerable of desired entity. The repository is supposed to prevent queries which may take too long to process. The QueryConstraints is as follow:
public class QueryConstraints<T>
{
public Expression<Func<T, bool>> WhereClause { get; }
public Expression<Func<T, bool>> SortClause { get; }
public int Skip {get;}
public int Take {get;}
...
some other fields
}
When a client creates a QueryConstraints it should assign expressions to WhereClause and SortClause properties and pass the object to the Find() method of the repository where the expressions are to be inspected and processed.
My question is:
I know that it is possible to use expression tree to inspect the body of the expression. However I don't know how to do it exactly to achieve my purpose. For example I want to prevent a query which has no index available for its fields. How should i do that?
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?
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.
I have an abstract class which gets implemented for all of my repositories - within there I have a property which returns a collection:
protected readonly IDbSet<T> _dbSet;
public virtual IEnumerable<T> GetSelection(Func<T, bool> where)
{
return _dbSet.Where(where);
}
Repo:
public class PostcodeDataRepository : EntityRepositoryBase<PostcodeData>, IPostcodeDataRepository
{
// Constructor in here
}
Now this works great:
var postcodeData = postcodeRespoistory.GetSelection(x=>x.Postcode == "SW1A 2TT");
(Yes it's uk postcode data, and yes there are nearly 2m rows in the table)
This works great but my problem is returning all of the data only for the application to then filter it is causing some performance issues (as you would expect!). I used from MiniProfiler and EFProf to confirm that it is effectively doing a select * from PostcodeData which isn't what I want it to do.
Has anyone any ideas how I can resolve this?
You need to use the Expression<Func<TSource, bool>> predicate:
public IEnumerable<T> GetSelection(Expression<Func<T, Boolean>> predicate)
{
return _dbSet.Where(where);
}
In my DB I have tables who have an attribute int DeleteState. I want a generic method to query those tables. In other words a method who does this: Context.Table.Where(x => x.DeleteState == 0).
I thought I could do this:
public static class Extensions
{
public static IQueryable<T> Exists<T>(this IQueryable<T> qry) where T : IDeletable
{
return qry.Where(x => x.DeleteState == 0);
}
}
Where IDeletable is this:
public interface IDeletable
{
int DeleteState { get; set; }
}
Now I only have to add the IDeletable in the EF model:
public partial class Table : EntityObject, IDeletable { ... }
I did this with the templating mechanism.
Unfortunately, it doesn't work :( It compiles fine, but throws at runtime:
Unable to cast the type 'Table' to type 'IDeletable'. LINQ to Entities only supports casting Entity Data Model primitive types
if I call it like that:
Context.Table.Exists();
How can I solve this problem? Could you think of a fix or a different method to achieve similar results? Thx
The problem you have is that the Entity Framework can only work with an Expression Tree. Your function executes a query directly instead of building an Expression Tree.
A simpler solution would be to add a Model Defined Function.
A model defined function can be called directly on an instance of your context.
Maybe:
public static IQueryable<T> Exists<T>(this IQueryable<T> qry)
{
return qry.Where(x => (!typeof(IDeletable).IsAssignableFrom(x.GetType()) || typeof(IDeletable).IsAssignableFrom(x.GetType()) && ((IDeletable)x).DeleteState == 0));
}
Tsss, this is the answer: Linq Entity Framework generic filter method
I forgot about the class here:
... where T : class, IDeletable
Have you tried converting your objects to IDeletable before you actually query? e.g.
public static IQueryable<T> Exists<T>(this IQueryable<T> qry)
{
return qry.Select<T, IDeletable>(x => x).Where(x => x.DeleteState == 0).Cast<T>();
}
I haven't tested this code, however, the error rings a bell and I remember I had to do something similar.