开发者

Possible to do this in linq?

开发者 https://www.devze.com 2023-02-18 01:35 出处:网络
I am wondering if something like this could be done(of course what I have written does not work but that\'s what I am essentially trying to achieve) .

I am wondering if something like this could be done(of course what I have written does not work but that's what I am essentially trying to achieve) .

var test = u.Owner;
a.Table.Where(u => test == true)

I have a linq query that I want to reuse(basically I got a query that I use 5 times) and the only thing that changes is what I am comparing against.

So in the above u.Owner is compared against true. In another query it would look the same but instead of u.Owner I might have u.Add == true or u.Edd == t开发者_如何学Crue.

So is there a way I can sort of reuse what I have. Of course in my code the query is a bit longer but I just shortened down.

Edit

Basically the whole query

 List<Permission> clearence = user.PermissionLevels.Where(u => u.Id == Id &&( u.Add == permissionNeeded || u.Permission.Name == PermissionTypes.Owner)).ToList();

permissionNeeded == Enum

So my orignal way of doing it was u.Permission.Name == permissionNeeded so I compared the enum value to a string.

Now my db model has change and I need to check against 5 different permissions separately that are bools and not strings.

u.Add = true || u.Owner == true;
u.Edit = true || u.Owner == true;
u.Delete= true || u.Owner == true;
u.View= true || u.Owner == true;

Thats all changes for the entire query so that's why I am trying to make it into one query(just like I had it before).

So I am thinking of having a switch statement. The method still takes in a permissionNeeded(enum) I then go through and determine what clause I need and some how insert it into the query.

switch(PermssionNeeded)
{
   case PermissionTypes.Add:
                            u.Add;
                            break;
  // all other cases here.
}


Take advantage of the fact that Where can have any function taking type Table as a parameter and returning a boolean to create a function like:

public IQueryable<Table> QueryTables(Func<Table, bool> testFunction)
{
    return a.Table.Where(testFunction).AsQueryable<Table>();
}

Edit: (in addition to edit to add AsQueryable above, which I earlier forgot)

If all you want to do is vary the boolean field used in the test, and you don't want to have to specify an entire function (which you're about to find out is much easier), you would need to use some reflection:

using System.Reflection;

public IQueryable<Table> QueryTables(PropertyInfo pi)
{
    return a.Table.Where(t => (bool)(pi.GetGetMethod().Invoke(t, null))).AsQueryable<Table>();
}

To construct the PropertyInfo object, use something like:

PropertyInfo pi = typeof(Table).GetProperty("Owner");

I prefer the earlier method, but I did want to show that something like this is at least possible.


If you only want to specify the property you are checking you can do

public IEnumerable<Table> GetTables(Func<Table,bool> getValue)
{
    return a.Table.Where(table => /*some common filter*/)
                  .Where(table => getValue(table))
}


var query = from u in uSrc join v in vSrc on u.ID equals v.RelatedUID
  where v.Valid && u.Created < DateTime.UtcNow.AddDays(-365)
  select u; // relatively complicated starting point.
var q1 = query.Where(u => u.Add); // must also have Add true
var q2 = query.Where(u => u.Test); // must also have Test true
var q3 = query.Where(u => u.ID < 50); // must also have ID < 50

And so on.

Edit:

Okay, so your starting query is:

List<Permission> clearence = student.PermissionLevels.Where(u => u.Id == Id &&( u.Add == permissionNeeded || u.Permission.Name == PermissionTypes.Owner)).ToList();

However, note that this creates a list, so any further work done on it will be a matter of Linq-to-objects. We'll come back to that in a minute, as it's sometimes good and sometimes not.

Now, if I understand you correctly, you need different sets for different cases, which you can do with your query as per:

var set0 = clearance.Where(u.Add = true || u.Owner == true);
var set1 = clearance.Where(u.Edit = true || u.Owner == true);
var set2 = clearance.Where(u.Delete= true || u.Owner == true);
var set3 = clearance.Where(u.View= true || u.Owner == true);

Now, this will work, but may not be the best approach. If we go back to the original query, we don't have to do ToList(), but can have:

IQueryable<Permission> clearence = student.PermissionLevels.Where(u => u.Id == Id &&( u.Add == permissionNeeded || u.Permission.Name == PermissionTypes.Owner));

Now, in the first case because we built a list we first got all values that matched the critera backed, and then stored it in memory, in clearance.

In the second case, clearance doesn't store any values at all, but instructions on how to get them.

The question is which is better to use. In the case where we are going to end up using the vast majority of the objects returned by the first query on its own, then there is a performance boost in using the list version, because they are loaded into memory only once, and then taken from memory without hitting the database again.

However, in most cases, it's better to do the second version for two reasons:

  1. We hit the database in each case, but only retrieve the objects needed in that case.
  2. We don't store anything in memory longer than necessary. Above a certain amount this is an important performance matter in itself.
  3. The time to first item is faster this way.
  4. We can further refine, for example if we do the following:

var trimmed = from set0 select new{u.Id, u.Permission.Name};

The we retrieve anonymous objects with Id and Name properties that are all we care about for a particular case, and not all of the relevant fields are retrieved from the database, or other source.


I've recently come to prefer a Dictionary over switch statements. You could store all your lambas in a Dictionary<PermissionNeeded, Func<User, bool>> that would look like this:

Dictionary<PermissionNeeded, Func<User, bool>> Permissions = 
    new Dictionary<PermissionNeeded, Func<User, bool>> {
        { PermissionNeeded.Add, u => u.Add }, // don't need to specify == true
        { PermissionNeeded.Edit, u => u.Edit }, 
        ... 
        etc
    };

And you would call it like this:

var clearance = Permissions[PermissionNeeded.Add](user);

or maybe

var clearance = Permissions[PermissionNeeded.Add](user) && Permissions[PermissionNeeded.Edit](user);

or perhaps

var clearance = Permission[PermissionNeeded.Add](user) || Permissions[PermissionNeeded.View](user);

and so on. Even if you don't have a user object, I think this would still be valid and make the code pretty easy to read, and if you have to modify your functions, its all in the dictionary...

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号