我没有很多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;
}
答案 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;
}