我将在这里使用一个简单的示例来说明我的问题。我正在尝试的是通过switch语句基于过滤的LINQ查询返回视图模型。我的代码是这样的:
var query = (
from b in _db.Books
join a in _db.Authors on b.AuthorId = a.Id
select new BookViewModel
{
AuthorName = a.Name,
BookName = b.Name
});
switch (currentUser.Role)
{
case "Admin": return query.ToList(); // Return all books for an admin.
case "Publisher": return query.Where(x => x.Publisher == currentUser.PublisherId).ToList();
default: throw new UnauthorizedAccessException(); // Given role is not authorized.
}
例如,在“ Publisher”案例语句中,我要过滤并通过当前用户的发布者ID返回查询。但是我无法用当前的代码设置来实现这一点,因为我通过LINQ查询选择的BookViewModel对象中不存在此属性。
我喜欢这种使用switch语句进行过滤的方式。我发现代码非常可读。无需我将额外的属性添加到视图模型中的最完美的方法是什么?
谢谢。
答案 0 :(得分:2)
如果我的理解正确,您可以将linq where
与某些逻辑配合使用。
var query = (
from b in _db.Books
join a in _db.Authors on b.AuthorId = a.Id
where (b.Publisher == currentUser.PublisherId && currentUser.Role == "Publisher") ||
(currentUser.Role == "Admin")
select new BookViewModel
{
AuthorName = a.Name,
BookName = b.Name
});
注意
linq中的Publisher
可以更改为正确的上下文值。
答案 1 :(得分:0)
当您说case "Publisher": return query.Where(x => x.Publisher == currentUser.PublisherId).ToList();
时,case语句x.Publisher
就是BookViewModel中的一个属性。
但是您没有像使用AuthorName
和BookName
那样用任何值初始化该属性,因此Publisher将为空。
如何在不将Publisher属性添加到视图模型中的情况下解决此问题,完全取决于该属性的存储方式/存储位置,即您未提供的信息。
答案 2 :(得分:0)
您可以提取if("knitr" %in% installed.packages()[,1]){
library(knitr)
}else{
install.packages("knitr")
library(knitr)
}
作为子查询来简化管理。我假设_db.Books
在您的Publisher
DbSet中。如果是这样,那么:
Books
答案 3 :(得分:0)
问题是您想从查询结果中使用属性Publisher
,但又不想从函数中返回Publisher
作为属性。
这意味着,在应用Where
时,Publisher
应该仍在您的项目中,但是在Where
之后,应该将其删除。
解决方案是:在Select
之后执行Where
:
(我更习惯于方法语法。当然,该方法也适用于查询语法
var initialQuery = db.Books
.join(db.Authors // join Books and Authors
book => book.AuthorId, // from every book take the AuthorId
author => author.Id // from every Author take the Id
(book, author) => new // when they match make one new object
{ // containing the matching book and author
PublisherId = book.PublisherId,
AuthorName = author.Name,
BookTitle = book.Title,
});
请注意,查询已创建,但尚未执行!您仍然可以使用其他LINQ语句扩展查询,而无需花费太多成本。
幸运的是,您的switch语句不会更改initialQuery的类型,因此您可以重复使用它:
switch (switch (currentUser.Role)
{
case "Admin":
// nothing to do
break;
case "Publisher":
initialQuery = initialQuery
.Where(joinResult => joinResult.Book.Publisher == currentUser.PublisherId);
break;
default:
throw new UnauthorizedAccessException();
}
现在执行选择:
问题!
尽管您没有这么说,但是
db.Books
和db.Authors
是 数据库。他们实现IQueryable<Book>
和IQueryable<Author>
。执行查询时,您只能使用 可以转换为SQL(或类似)的语句。这表示new BookViewModel
是IQueryable时不能使用。
解决方案:您必须先完成AsEnumerable
,然后才能使用new BookViewModel
进行最终选择
var queryResult = initialQuery
// Transfer only the data you actually plan to use to your process
.Select(joinResult => new
{
AuthorName = joinResult.AuthorName,
BookName = joinResult.BookTitle,
})
// move the selected data to local process in efficient way
.AsEnumerable()
// now you are free to use your local constructor:
.Select(fetchedData => new BookViewModel()
{
BookName = fetchedData.BookName,
AuthorName = fetchedData.AuthorName,
});
顺便说一句:仍然没有执行查询。立即执行并返回所有获取的数据:
return queryResult.ToList();
答案 4 :(得分:0)
var query = (
from b in _db.Books
join a in _db.Authors on b.AuthorId = a.Id
select new BookViewModel
{
AuthorName = a.Name,
BookName = b.Name
});
if (currentUser.Role.Equals("Admin")
{
query = query.Where(x => x.Publisher == currentUser.PublisherId);
}
return query.ToList();
通过使用Authorize
属性修饰该方法,可以将授权与逻辑分开(如果您使用的是MVC / MVC Core)。
答案 5 :(得分:0)
在LINQ中进行联接时,编译器会创建一个匿名对象,该对象称为透明标识符,这使它可以在范围内保留多个范围变量。当您从各个部分构建查询表达式时,可以包括LINQ表达式和扩展方法调用的混合(如下所示),您可以自己执行此操作,尽管在这种情况下,我猜它可能是一个不透明的标识符。
我同意@ErikE的观点,在进行到这一步之前,您的访问检查应该已经完成,所以下面的代码反映了这一点。
var query =
from b in _db.Books
join a in _db.Authors on b.AuthorId = a.Id
select new { a, b };
// We'll test just the role that needs additional filtering. Here,
// 'x' is the aforementioned opaque identifier.
if (currentUser.Role == "Publisher")
query = query.Where(x => x.b.PublisherId == currentUser.PublisherId);
从这里开始,您可以在此基础上进一步扩展,例如将结果限制为由特定代理人代表的人所图解的书籍。这可能超出了您的需求,但它展示了该技术的灵活性。
if (illustratorAgentId != null)
{
// Join the Illustrators table and test its AgentId. Since we only need the book
// and author, we can continue to project 'x'.
query =
from x in query
join ill in _db.Illustrators on x.b.IllustratorId equals ill.IllustratorId
where ill.AgentId == illustratorAgentId
select x;
}
然后,当您完成查询的构建时,可以将范围变量的属性(通过不透明标识符)投影为BookViewModel
。
IQueryable<BookViewModel> result =
from x in query
select new BookViewModel
{
AuthorName = x.a.Name,
BookName = x.b.Name
};
然后您可以在result
上调用任何常用的验证方法,在这种情况下,可能是ToList
。