[英]Filtering A C# LINQ Statement With A Switch Statement
我將在這里使用一個簡單的示例來說明我的問題。 我正在嘗試的是通過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語句進行過濾的方式。 我發現代碼非常可讀。 在無需將額外屬性添加到視圖模型的情況下,最理想的實現此目的的方法是什么?
謝謝。
如果我理解正確,則可以使用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
可以更改為正確的上下文值。
case語句case "Publisher": return query.Where(x => x.Publisher == currentUser.PublisherId).ToList();
當您說x.Publisher
時,它指的是BookViewModel中的屬性。
但是您沒有像使用AuthorName
和BookName
那樣使用任何值初始化該屬性,因此Publisher將為null。
如何在不將Publisher屬性添加到視圖模型的情況下解決此問題,完全取決於該屬性的存儲方式/存儲位置,即您未提供的信息。
您可以將_db.Books
提取為子查詢,以便於管理。 我假設Publisher
在您的Books
DbSet中。 如果是這樣,那么:
var bookQueryable = _db.Books.AsQueryable();
switch (currentUser.Role)
{
case "Admin": break;
case "Publisher": bookQueryable = bookQueryable.Where(x => x.Publisher == currentUser.PublisherId);
default: throw new UnauthorizedAccessException(); // Given role is not authorized.
}
var query = (
from b in bookQueryable
join a in _db.Authors on b.AuthorId equals a.Id
select new BookViewModel
{
AuthorName = a.Name,
BookName = b.Name
});
return query.ToList();
問題是您想從查詢結果中使用屬性Publisher
,但又不想從函數中將Publisher
作為屬性返回。
這意味着,當Where
被應用, Publisher
仍然應該在你的項目,但之后Where
它應該被刪除。
解決方案是:在Where
之后執行Select
:
(我更習慣於方法語法。當然,該方法也適用於查詢語法
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(或類似語言)的語句。 這意味着當它是IQueryable時,您不能使用new BookViewModel
。
解決方案:您必須將其設置為AsEnumerable
,然后才能使用new BookViewModel
最終的Select。
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();
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)。
當您在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
為BookViewModel
。
IQueryable<BookViewModel> result =
from x in query
select new BookViewModel
{
AuthorName = x.a.Name,
BookName = x.b.Name
};
然后,您可以對result
調用任何常規的驗證方法,在這種情況下,可能是ToList
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.