合并2 C#IQueryable

时间:2015-03-06 18:11:06

标签: c# asp.net linq

我没有很多IQueryable的经验

我要做的是根据传入的约束列表搜索用户,这些约束可以是用户名或电话号码。根据类型,我想返回有限的信息。然后,我想将3个IQueryables合并为一个,并将具有匹配id和用户名的条目组合起来以保持最多信息。

这是我到目前为止所拥有的:

public IQueryable<User> Search(String[] criteria)
{
        var query = Database.Get<User>();

        IQueryable<User> phoneQuery = null;
        IQueryable<User> emailQuery = null;
        IQueryable<User> nameQuery = null;

        foreach (String str in criteria)
        {
            // Check if it is a phone number
            if (Regex.IsMatch(str, @"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
            {
                phoneQuery = query.Where(
                       u => u.PhoneNumber.ToLower() == (str))
                       .Select(i => new User
                       {
                           Id = i.Id,
                           UserName = i.Name,
                           PhoneNumber = i.PhoneNumber
                       })
            }
            // check if it is an email 
            else if (criteria.Contains("@"))
            {
                emailQuery = query.Where(
                       u => u.Email.ToLower() == (str))
                       .Select(i => new User
                       {
                           Id = i.Id,
                           UserName = i.Name,
                           Email = i.Email
                       })
            }
            else
            {
                nameQuery = query.Where(
                       u => u.UserName.ToLower() == (str))
                       .Select(i => new User
                       {
                           Id = i.Id,
                           UserName = i.Name,
                       })
            }
        }
        // Merge the 3 queries combining entries if the username and id match and maintain the maximum amount of information

        return query;

    }

2 个答案:

答案 0 :(得分:2)

您的代码存在一些问题:

ToList()将执行查询。如果稍后调用AsQueryable(),则只需在本地对象上创建对象查询。这基本上会失去IQueryable的概念,因此您最好删除所有ToList()AsQueryable()来电。

您可以将其设为单个查询,而不是合并三个查询,如下所示:

Expression predicateBody = Expression.Constant(false);
Expression userParameter = Expression.Parameter("user", typeof(User));
Expression userUserName = Expression.MakeMemberAccess(...);
Expression userPhoneNumber = Expression.Cal(...);
Expression userEmail = Expression.Call(...);

foreach (String str in criteria)
{
    // Check if it is a phone number
    if (Regex.IsMatch(str, @"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
    {
         predicateBody = Expression.Or(predicateBody, Expression.Equals(userPhoneNumber, Expression.Constant(str)));
    }
    // check if it is an email 
    else if (criteria.Contains("@"))
    {
         predicateBody = Expression.Or(predicateBody, Expression.Equals(userEmail, Expression.Constant(str)));
    }
    else
    {
         predicateBody = Expression.Or(predicateBody, Expression.Equals(userUserName, Expression.Constant(str)));
    }
}

return query.Where(Expression.Lambda<Func<User, bool>>(predicateBody, userParameter))
     .GroupBy(u => u.Id)
     .Select(users => new User()
          {
               Id = users.Key,
               UserName = users.Select(u => u.UserName).Intersect(criteria).FirstOrDefault(),
               Email = users.Select(u => u.Email).Intersect(criteria).FirstOrDefault(),
               PhoneNumber = users.Select(u => u.PhoneNumber).Intersect(criteria).FirstOrDefault()
          });

编辑抱歉,我误解了合并问题。

Edit2 如果标准事先被排序,那么还有一个解决方案不需要手动创建表达式树。

编辑3 我知道,我忘记了信息有限的部分。

var phoneNumbers = new List<string>();
var emails = new List<string>();
var userNames = new List<string>();

foreach (var str in criteria)
{
    // Check if it is a phone number
    if (Regex.IsMatch(str, @"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
    {
         phoneNumbers.Add(criteria);
    }
    // check if it is an email 
    else if (criteria.Contains("@"))
    {
         emails.Add(crietria);
    }
    else
    {
         userNames.Add(criteria);
    }
}

return query.Where(u => phoneNumbers.Contains(u.PhoneNumber)
       || emails.Contains(u.Email)
       || userNames.Contains(u.UserName))
       .GroupBy(u => u.Id)
       .Select(users => new User()
              {
                   Id = users.Key,
                   UserName = users.Select(u => u.UserName).Intersect(userNames).FirstOrDefault(),
                   Email = users.Select(u => u.Email).Intersect(emails).FirstOrDefault(),
                   PhoneNumber = users.Select(u => u.PhoneNumber).Intersect(phoneNumbers).FirstOrDefault()
              });

答案 1 :(得分:-1)

这就是我最终的目标。

public IQueryable<User> Search(String[] criteria)
        {
            var query = Database.Get<User>();
            List<User> res = new List<User>(); 
            foreach (String str in criteria)
            {
                // Check if it is a phone number

                if (Regex.IsMatch(str, @"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
                {
                    var users = query.Where(
                           u => u.PhoneNumber.ToLower() == (str))
                           .Select(i => new User
                           {
                               Id = i.Id,
                               UserName = i.Name,
                               Email = null,
                               PhoneNumber = i.PhoneNumber
                           });

                    // Multiple users can have the same phone so add all results
                    foreach (User u in users)
                    {
                        if (u != null) { res.Add(u); }
                    }

                }
                // Check if it is an email match 
                else if (str.Contains("@"))
                {
                    var user = query.Where(
                           u => u.Email.ToLower() == (str))
                           .Select(i => new User
                           {
                           Id = i.Id,
                           UserName = i.Name,
                           Email = i.Email,
                           PhoneNumber = null
                       }).SingleOrDefault<User>(); // Only one user can use an email

                if (user != null) { res.Add(user); }
            }
            // Otherwise it is a username
            // NOTE: If a username is all digits and dashes it won't be 
            // searched for because it is interpreted as a phone number!
            else
            {
                var user = query.Where(
                       u => u.UserName.ToLower() == (str))
                       .Select(i => new User
                       {
                           Id = i.Id,
                           UserName = i.Name,
                           Email = null,
                           PhoneNumber = null
                       }).SingleOrDefault<User>(); // Only one user can use an email

                if (user != null) { res.Add(user); }
            }
        }

        query = res.AsQueryable(); 

        // Group the results by username and id and return all information that was found
        query = from u in query
                group u by new
                {
                    u.Id,
                    u.UserName
                } into g
                select new User()
                {
                    Id = g.Key.Id,
                    UserName = g.Key.UserName,
                    Email = g.Select(m => m.Email).SkipWhile(string.IsNullOrEmpty).FirstOrDefault(), 
                    PhoneNumber = g.Select(m => m.PhoneNumber).SkipWhile(string.IsNullOrEmpty).FirstOrDefault()
                };

        return query;
    }