开发者

Custom Linq extension method for jqGrid select list

I have a custom method for getting a string which is used with jqGrid for select lists for searching.

The required string format is ":All;value1:text1;value2:text2" I currently have the following method to achieve this in a reusable way:

public static string jqGridFilterSelectList<TSource>(this IQueryable<TSource> source,Expression<Func<TSource, string>> selector)
{
     return string.Join(";", source.Select(selector).Distinct().ToArray()); 
}

This works OK, but it requires the call to be 开发者_JAVA百科like this:

string filterSelectList = service.GetAllUsers().jqGridFilterSelectList(x=>x.User + ":" + x.User);

I'm not very happy with having to use a selector like x=>x.User + ":" + x.User. I'd like to convert this into 2 overloads like this (pseudo code!!)

// case where we want the value and text of the select list to be the same
public static string jqGridFilterSelectList<TSource>(this IQueryable<TSource> source,Expression<Func<TSource, string>> selector)
{
     return string.Join(";", source.Select(selector + ":" + selector).Distinct().ToArray()); 
     //obviously this won't work, but is it possible in some other way while executing it on the database still? Also is it possible to insist the selector only contains 1 column.
}

//case where we want different text and value
public static string jqGridFilterSelectList<TSource>(this IQueryable<TSource> source,Expression<Func<TSource, string>> textSelector,Expression<Func<TSource, string>> valueSelector)
{
     return string.Join(";", source.Select(valueSelector + ":" + textSelector).Distinct().ToArray()); 
}

I suppose i could probably achieve this using dynamic linq, but i'm interested to know if this is possible.


If I understand correctly, you've almost written it:

public static string jqGridFilterSelectList<TSource>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, string>> selector
)
{
     var compiled = selector.Compile();
     return string.Join(";", 
         source.Select(x => compiled(x) + ":" + compiled(x))
             .Distinct().ToArray()
     ); 
}

//case where we want different text and value
public static string jqGridFilterSelectList<TSource>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, string>> textSelector, 
    Expression<Func<TSource, string>> valueSelector
)
{
     return string.Join(";", 
         source.Select(x => valueSelector.Compile()(x) + ":" + textSelector.Compile()(x))
             .Distinct().ToArray()
     ); 
}

You just need to invoke your selector functions to get the values. Let me know if I misunderstand your question.

For the second question, is it possible to insist the selector only contains 1 column, the simple answer is no - the selector can be virtually any valid Func that takes TSource and returns string and there's no way to limit what can be done inside (unless you want to examine the expression tree provided, but it will be more complicated than the outcome, for sure).

EDIT: OK, so this is some kind of SQL LINQ provider, Entity Framework? So let's try a bit differently. I don't know whether it'll work, but let's try to fetch the data first and concatenate on objects:

public static string jqGridFilterSelectList<TSource>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, string>> selector
)
{
     var compiled = selector.Compile();
     return string.Join(";", 
         source.Distinct().AsEnumerable()
             .Select(x => compiled(x) + ":" + compiled(x))
             .ToArray()
     ); 
}

//case where we want different text and value
public static string jqGridFilterSelectList<TSource>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, string>> textSelector, 
    Expression<Func<TSource, string>> valueSelector
)
{
     return string.Join(";", 
         source.Distinct().AsEnumerable()
             .Select(x => valueSelector.Compile()(x) + ":" + textSelector.Compile()(x))
             .ToArray()
     ); 
}

The additional ToEnumerable call switches between database an in-process processing. Assuming that your selectors are deterministic and don't have any side effects, you can operate on already fetched TSource items.


One more way to implement what you need is to use dataUrl and buildSelect parameters of the searchoptions.

In the case the value string in the form ":All;value1:text1;value2:text2" will not be built till it is really needed (till the first searching). When the user click on the search dialog the jqGrid get the data from the dataUrl. The server method can have very clear interface and return JSON representation of List<string>. On the client side you define buildSelect event handle which convert the the ["text1", "text2", ...] to the <select>. It's all. An example the usage of this ways you can find here. In comparing with the example you need only add additional option <option value="">All</option> in the select element.

Sometimes you should additionally use ajaxSelectOptions jqGrid parameter to customize the corresponding $.ajax request which get the data for the select list.

The advantages of the way:

  1. The server method providing the data for select list can have very clear interface and return just List.
  2. The size of data send from the server will be reduced because you don't send the "value1" and "text1" twice because of the values are the same as the text in your case.
  3. You can use the same method for editoptions in case of your grid use any editing method (inline, form or cell editing). In another implementation of buildSelect you should only skip inserting of <option value="">All</option> in the select list.
  4. The data for select element will be build only if there really needed: at the first usage of searching.
  5. The HTTP GET response from the server for select list can be cached automatically and if you need the same select data in another grid the previous server response can be get directly from the local browser cache.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜