简体   繁体   中英

How to parametrize a query in Entity Framework?

I am new to EF. I have a table with a list of projects. I have found a query in my software that finds all projects .

public Project[] FindAll()
{
    var projects = new List<Project>();
    using (var db = new ProjetDbConext())
    {
        var qProjects = from project in db.ProjectSet
            where project.CreateDateTime != null
            select project;
        projects = qProjects.ToList();
    }
    return projects.ToArray();
}

This seems to be fine , but I am not sure how to parametrize it. I need this because I am implementing a search feature trying to re use some query logic from EF.

This takes a List of tuples . Each tuple basically has an attribute and a list of search terms.

eg. Tuple(FirstName , { Prasaanth ,Bill } ; Tuple( LastName , { Neelakandan , Gates } ;

This means I need to write a select query where I search projects where FirstName is Prasaanth or Bill . If the list has only one term.

eg. Tuple( Company , { Microsoft} ; then i need to search only one where condition in my query.

 public Project[] LoadSearchProjects(List<System.Tuple<string, List<string>>> searchTerms)
        {
            var projects = new List<Project>();

            using (var db = new ProjetDbConext())
            {
                foreach (System.Tuple<string, List<string>> pair in searchTerms)
                {

                    string attribute = pair.Item1;
                    List<string> terms = pair.Item2;

                      /// logic here
                 }  
            }
            return projects.ToArray();

        }

I can always write an if condition where I do :

if(attribute.equals("FirstName"){

 // query project.FirstName in the where conditon 


}

But I have too many attributes to search on.

I know the ADO.NET way of doing this :

 mycommands = new SqlCommand(" select projects from from Persons where '"+attibute+"' =  some search terms ... 

I don't know how to do something like this in my EF query.

1 ) Is there a way EF allows me to do the search on dynamic attributes ? or parametrize using '"+attribute+"' ??

2) Is there a better data structure I could use to simplify my structure instead of using List<Tuple<string, List<string>> ?

3) I was recommend to use 3rd party LINQKit or dynamic linq but am not sure how to integrate that to EF querying.

My apologies if much of this sounds like collegeboy code. Please let me know if any additional details needed.

Regards, Prasaanth

UPDATE : Working method as per Andriy's answer. My question here is this doesnt work if any particular entry in my database say Name is Null.

 private static Expression<Func<TEntity, bool>> BuildStringFilter<TEntity, TProp>(
            Tuple<string, List<string>> filter)
        {
          // entity is the Project table
            var entity = Expression.Parameter(typeof (TEntity));
            var prop = Expression.Property(entity, filter.Item1);

            //check if contains returns true

            var body = filter.Item2
                .Select(v => Expression.Equal(Expression.Call(prop,
                    typeof (String).GetMethod("Contains"),
                    new Expression[] { Expression.Constant(v) }), Expression.Constant(true)))
                .Aggregate(Expression.Or);



            var result = (Expression<Func<TEntity, bool>>) Expression.Lambda(body, entity);
            return result;
        }

Any way I can modify the expression so that the Contains method :

prop, typeof (String).GetMethod("Contains"), new Expression[] { Expression.Constant(v)

works if the value of the attribute (prop) is null ?

You can build filter expression using snippet:

public static Expression<Func<TEntity, bool>> BuildFilter<TEntity, TProp>(
    KeyValuePair<string, IEnumerable<TProp>> filter)
{
    var entity = Expression.Parameter(typeof(TEntity));
    var prop = Expression.Property(entity, filter.Key);

    var body = filter.Value
        .Select(v => Expression.Equal(prop, Expression.Constant(v)))
        .Aggregate((curr, next) => Expression.Or(curr, next));

    var result = (Expression<Func<TEntity, bool>>)Expression.Lambda(body, entity);
    return result;
}

And call it like:

var filter = new KeyValuePair<string, IEnumerable<string>> (
    "FirstName",
    new [] {"Alice", "Bob"}
);

var predicate = BuildFilter<Item, string>(filter);
var result = ctx.Items.Where(predicate);

Also, see How to: Use Expression Trees to Build Dynamic Queries .

I'm spitballing here, but I think something like this would be simpler:

public Project[] Find(Expression<Func<Project, bool> filter = null)
{    
    using (var db = new ProjetDbConext())
    {
        var query = db.ProjectSet.Where(p => p.CreateDateTime != null);

        if(filter != null)
            query = query.Where(filter);

       return query.ToArray();
    }
}

Use it like:

var projects = repo.Find(p => p.id > 100);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM