2015-09-04 68 views
4

我有一個綁定列表視圖到IQueryable一個ASP.NET頁面:使用返回隨機排一個IQueryable

<asp:ListView ID="productList" runat="server" 
    DataKeyNames="ProductID" GroupItemCount="6" 
    ItemType="products.Models.Product" SelectMethod="GetProducts"> 
    <EmptyDataTemplate> 
     <div> 
      Sorry, no products available 
     </div> 
    </EmptyDataTemplate> 
    <GroupTemplate> 
     <ul id="da-thumbs" class="da-thumbs"> 
     <div id="itemPlaceholderContainer" runat="server" class="text-center"> 
      <div id="itemPlaceholder" runat="server"></div> 
     </div> 
     </ul> 
    </GroupTemplate> 
    <ItemTemplate> 
     <li> 
      <img src='/products/Catalog/Images/Thumbs/<%#:Item.ImagePath%>' class="img-responsive"/> 
      <div> 
       <span class="hidden-xs">Short Description</span> 
       <span class="spacer visible-xs"></span> 
       <a href="<%#: GetRouteUrl("ProductByNameRoute", new {productName = Item.ProductName}) %>" class="btn btn-success btn-block align-mid">View Details</a>  
       <a href="<%#:Item.ShortURL %>" class="btn btn-success btn-block align-mid"><%#:Item.DownloadText%></a> 
      </div> 
     </li> 
    </ItemTemplate> 
    <LayoutTemplate> 
     <div class="row"> 
      <asp:PlaceHolder ID="groupPlaceholderContainer" runat="server"> 
       <div id="groupPlaceholder" runat="server"></div> 
      </asp:PlaceHolder> 
     </div>     
    </LayoutTemplate> 
</asp:ListView> 
<asp:DataPager ID="it" runat="server" PagedControlID="productList" PageSize="6" class="btn-group pager-buttons pagination pagination-large"> 
    <Fields> 
     <asp:NextPreviousPagerField ShowLastPageButton="False" ShowNextPageButton="False" ButtonType="Button" ButtonCssClass="btn" RenderNonBreakingSpacesBetweenControls="false" /> 
     <asp:NumericPagerField ButtonType="Button" RenderNonBreakingSpacesBetweenControls="false" NumericButtonCssClass="btn" CurrentPageLabelCssClass="btn disabled" NextPreviousButtonCssClass="btn" /> 
     <asp:NextPreviousPagerField ShowFirstPageButton="False" ShowPreviousPageButton="False" ButtonType="Button" ButtonCssClass="btn" RenderNonBreakingSpacesBetweenControls="false" /> 
    </Fields> 
</asp:DataPager> 

GetProducts()定義如下:

public IQueryable<Product> GetProducts(
         [QueryString("id")] int? categoryId, 
         [RouteData] string categoryName) 
{ 
    var _db = new products.Models.ProductContext(); 
    IQueryable<Product> query = _db.Products; 

    if (categoryId.HasValue && categoryId > 0) 
    { 
     query = query.Where(p => p.CategoryID == categoryId); 
    } 

    if (!String.IsNullOrEmpty(categoryName)) 
    { 
     query = query.Where(p => 
          String.Compare(p.Category.CategoryName, 
          categoryName) == 0); 
    } 

    var random = new Random(); 
    query = query.OrderBy(product => random.Next()).Where (p => p.Active == 1); 
    return query; 
} 

的問題是Random()不在這裏工作。當我運行我的應用程序時,出現錯誤

LINQ to Entities無法識別方法'Int32 Next()'方法,並且此方法無法轉換爲存儲表達式。

也許一個解決方案是將其更改爲IEnumerable但後來我需要IQueryable尋呼。

更新:使用塔希爾的解決方案後,這是內部查詢我得到:

{SELECT 
    [Project1].[ProductID] AS [ProductID], 
    [Project1].[ProductName] AS [ProductName], 
    [Project1].[Description] AS [Description], 
    [Project1].[ImagePath] AS [ImagePath], 
    [Project1].[DownloadText] AS [DownloadText], 
    [Project1].[DownloadURL] AS [DownloadURL], 
    [Project1].[ShortURL] AS [ShortURL], 
    [Project1].[UnitPrice] AS [UnitPrice], 
    [Project1].[CategoryID] AS [CategoryID], 
    [Project1].[Active] AS [Active], 
    [Project1].[ShortDescription] AS [ShortDescription], 
    [Project1].[Priority] AS [Priority] 
    FROM (SELECT 
     RAND() AS [C1], 
     [Extent1].[ProductID] AS [ProductID], 
     [Extent1].[ProductName] AS [ProductName], 
     [Extent1].[Description] AS [Description], 
     [Extent1].[ImagePath] AS [ImagePath], 
     [Extent1].[DownloadText] AS [DownloadText], 
     [Extent1].[DownloadURL] AS [DownloadURL], 
     [Extent1].[ShortURL] AS [ShortURL], 
     [Extent1].[UnitPrice] AS [UnitPrice], 
     [Extent1].[CategoryID] AS [CategoryID], 
     [Extent1].[Active] AS [Active], 
     [Extent1].[ShortDescription] AS [ShortDescription], 
     [Extent1].[Priority] AS [Priority] 
     FROM [dbo].[Products] AS [Extent1] 
     WHERE 1 = [Extent1].[Active] 
    ) AS [Project1] 
    ORDER BY [Project1].[C1] ASC} 

更新:這是使用修改後的代碼塔希爾的和JCL的建議:

if (!String.IsNullOrEmpty(categoryName)) 
{ 
    Session["rand"] = null; 
    query = query.Where(p => 
         String.Compare(p.Category.CategoryName, 
         categoryName) == 0); 
} 

var seed = 0; 

if (Session["rand"] == null) 
{ 
    seed = (int)DateTime.Now.Ticks % 9395713;//a random number 
    Session["rand"] = seed; 
} 

var readSeed = (int)Session["rand"]; 
query = query 
    .OrderBy(product => SqlFunctions.Rand(product.ProductID * readSeed % 577317)); 
return query; 
+0

你想基於隨機屬性對行進行排序嗎? –

+0

目前查詢按特定順序返回行,我認爲這是基於productId - 1,2,3,4,5等。我想對結果進行隨機洗牌,以便結果不以特定順序返回。它可以是5,2,4,1,3或者下一輪運行3,5,1,4,2等等。 – CuriousDev

+0

IWantToLearn你的問題是關於你的代碼中的錯誤,而不是隨機數字的問題! – Arash

回答

4

EF有一類支持的函數叫做SqlFunctions。你可以使用這個類的Rand功能:

query = query.OrderBy(product => SqlFunctions.Rand()).Where (p => p.Active == 1); 

不要忘記添加

using System.Data.Entity.Core.Objects; 

這種方法的問題是from this source

RAND的重複調用()在單個查詢中會產生相同的值。

我不認爲有任何解決辦法。解決方法是在原始SQL命令中使用`Newid(),我認爲這對您沒有用,或者加載查詢數據,然後將內存中的行進行整理。

我身邊嘗試這項工作,它似乎工作:

var seed = (int)DateTime.Now.Ticks % 9395713;//a random number 
query = query 
    .OrderBy(product =>SqlFunctions.Rand(product.ProductId * seed % 577317)) 
    .Where (p => p.Active == 1); 

這一招將強制SQL Server來生成每行一個新的隨機值,因爲各行上ProductId改變了這種改變Rand種子強制sql服務器產生新的隨機值。

您可以探索seed變量的不同值以獲得更好的結果。

編輯
爲了讓每個類別的種子,你可以在Application創建種子的高速緩存或簡單地創建的(類別,種子)對,是這樣的一個靜態辭典:

在控制器如果您使用MVC或頁面類,如果您使用ASP.NET:在動作或方法

static Dictionary<string, int> categorySeeds = new Dictionary<string, int>(); 

int seed = 0; 
if(categorySeeds.ContainsKey(categoryName)) 
{ 
    seed = categorySeeds[categoryName]; 
} 
else 
{ 
    seed = (int)DateTime.Now.Ticks % 9395713;//a random number 
    categorySeeds.Add(categoryName, seed); 
} 
//rest of the code 
+0

我試過了,但結果不是隨機的。它們以相同的順序進來 - var random = new Random(); query = query.OrderBy(product => SqlFunctions.Rand())。其中(p => p.Active == 1); 返回查詢; – CuriousDev

+0

我已經更新了使用解決方案後獲得的內部查詢的問題。你能告訴我爲什麼行不是隨機發生的,要改變什麼? – CuriousDev

+1

感謝@Taher種子部分的技巧,但只要我打第2頁,洗牌再次發生。如何確保查詢第一次發生隨機播放,然後與現在存在於內存中的行分頁?正如漢斯所提到的那樣,使用當前的解決方案,第2頁包含的內容也在第1頁。當您回到第1頁時,情況就不同了! – CuriousDev

-2

恰克你的代碼,這

var random = new Random(); 
var randomNumber=random.Next(); 
query = query.OrderBy(product => randomNumber).Where (p => p.Active == 1); 
+0

儘管代碼無誤地運行,但數據的順序保持不變。目前查詢以特定的順序返回行,我認爲這是基於productId - 1,2,3,4,5等。我如何洗牌結果,以便結果不以特定順序返回。它可以是5,2,4,1,3或者下一輪運行3,5,1,4,2等等。 – CuriousDev

+0

IWantToLearn您應該保留一個Random實例並在同一個實例上繼續使用Next。每次都不要獲得隨機類的實例。 – Arash

+0

這不會洗牌任何東西。你可以使用'var randomnumber = 4;'並且具有完全相同的效果。該值在OrderBy內不會更改。 –

1

老實說,最好的方法是使用SQL Server中的存儲過程,將隨機種子作爲參數傳遞給它(並且在您想要再次洗牌時在代碼中更改它)。除非你將相同的種子傳遞給你的隨機,否則分頁將不起作用(這似乎是一個要求),因爲每次查詢刷新時,你將得到不同的排序。

如果你不能在內存中存儲所有的數據(並且在內存中的對象上而不是在EF上),最好的解決方案是直接在SQL Server中完成,並且只從EF中讀取結果該存儲過程。

或者,如果修改您的實體是合理的,那麼對你的實體整型字段,並填寫隨機值這個實體(應該是快,如果通過SQL據我所知直接完成,因爲EF沒有做批量更新),每次你想要洗牌時,按順序排列(這可能在多用戶環境中很棘手,但可以完成,而且你不應該在乎幾個不同的用戶是否得到相同的排序,如果你?)。

這些將是在服務器上以真正的僞隨機/混洗順序進行分頁工作的唯一方法。

同樣,如果表格不是很大(並且不是),並且您可以將整個表格(無分頁)和頁面下載到內存集合中,那很容易。