2012-05-01 4 views
2

私は、別のステートメントを最後に追加する必要があるときにMVCコントローラにポストバックする必要があるクエリステートメントのリストを持っています。私が今作成しようとしているテストでは、ページはフィルタのリストから始まります。この例を実行すると、ページがこのようフィルタのフィールドを作成します。ASP.NETとMVC 3を使用して、リスト内の各項目の値として配列を持つListが正しくバインドされるように、隠しフィールドを作成するにはどうすればよいですか?

<input id="filters_0__PropertyName" name="filters[0].PropertyName" type="hidden" value="State"> 
<input id="filters_0__Operator" name="filters[0].Operator" type="hidden" value="="> 
<input id="filters_0__Value" name="filters[0].Value" type="hidden" value="CA"> 

をしかし、私は、フォームが実際にコントローラにポストバックを参照する場合、リストはと戻ってくる:

PropertyName = "State" 
Operator = "=" 
Value= "new string[1]" // The value comes back in the first index of the array 

私は配列として値のパラメータをキャストし、値を取得できるように最初のインデックスを取る必要があります。これは問題ないが理想的ではない。主な問題は、FilterFieldにValueプロパティの整数または文字列の配列が含まれている場合に発生します。その場合、HTMLは次のようになります。

<input id="filters_2__PropertyName" name="filters[3].PropertyName" type="hidden" value="Schedule_Num"> 
<input id="filters_2__Operator" name="filters[3].Operator" type="hidden" value="IN"> 
<input id="filters_2__Value" name="filters[3].Value" type="hidden" value="System.Int32[]"> 

この値には、オブジェクト型が含まれ、値はまったく含まれません。だからモデルバインダーは混乱し、すべてが壊れます。この値のリストをビューに結びつける簡単な方法はありますか?

FilterField.cs:

public class FilterField 
{ 
    public string PropertyName { get; set; } 
    public string Operator { get; set; } 
    public object Value { get; set; } 

    public FilterOutput GetOutput() 
    { 
     return new FilterOutput(PropertyName, Operator, Value); 
    } 
} 

FilterOutput.cs

public class FilterOutput 
{ 
    private List<QueryProperty> _properties = new List<QueryProperty>(); 
    private string _propertyName; 
    private string _op; 
    private object _value; 

    public string Sql { get; set; } 
    public QueryProperty[] Properties { get; set; } 

    public FilterOutput(string propertyName, string op, object value) 
    { 
     var sql = "[{0}] {1} {2}"; 
     Sql = string.Format(sql, propertyName, op, FormatParameter(propertyName, op, value)); 
     Properties = _properties.ToArray(); 
    } 

    private string FormatParameter(string propertyName, string op, object value) 
    { 
     _properties.Clear(); 
     var sb = new StringBuilder(); 
     switch (op.ToUpper()) 
     { 
      case "IN": 
       { 
        var values = value as Array; 
        sb.Append("{"); 
        var inCount = 0; 
        foreach (var v in values) 
        { 
         var pName = propertyName + inCount; 
         if (inCount == 0) 
          sb.Append("@" + pName); 
         else 
          sb.Append(",@" + pName); 
         _properties.Add(new QueryProperty { Name = pName, Value = v }); 
         inCount++; 
        } 
        sb.Append("}"); 
       } 
       break; 
      case "LIKE": 
       if (value.ToString().Contains("_")) 
        sb.Append("@" + propertyName); 
       else 
        sb.Append("'%' + @" + propertyName + " + '%'"); 
       _properties.Add(new QueryProperty { Name = propertyName, Value = value }); 
       break; 
      case "BETWEEN": 
       var range = value as Array; 
       var betweenCount = 0; 
       foreach (var r in range) 
       { 
        if (betweenCount > 0) 
         sb.Append(" AND "); 
        sb.Append("@" + propertyName + betweenCount); 
        _properties.Add(new QueryProperty { Name = propertyName + betweenCount, Value = r }); 
        betweenCount++; 
       } 
       break; 
      default: 
       sb.Append("@" + propertyName); 
       _properties.Add(new QueryProperty { Name = propertyName, Value = value }); 
       break; 
     } 
     return sb.ToString(); 
    } 

    public string ConvertToSql() 
    { 
     var filterOutput = this; 
     var output = filterOutput.Properties.Aggregate(filterOutput.Sql, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value))); 
     output = output 
      .Replace("[", "t.").Replace("]", "") // Convert [text] to t.text 
      .Replace("{", "(").Replace("}", ")") // Convert {'text1','text2'} to ('text1','text2') 
      .Replace("'%' + '", "'%").Replace("' + '%'", "%'"); // Convert '%' + text + '%' to '%text%' 
     return " AND " + output; 
    } 

    public override string ToString() 
    { 
     var filterOutput = this; 
     return filterOutput.Properties.Aggregate(filterOutput.Sql, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value)).Replace("'%' + '", "'%").Replace("' + '%'", "%'")); 
    } 

    private string FormatObjectToString(object value) 
    { 
     if (value is int) 
      return value.ToString(); 
     return String.Format("'{0}'", value); 
    } 
} 

HomeController.cs

public ActionResult TestQuery(DateTime date) 
{ 
    var builder = new QueryBuilder(_repo, "INFO", date); 

    builder.AddFilters(
      new FilterField 
       { 
        PropertyName = "State", 
        Operator = "=", 
        Value = "CA" 
       }, 
      new FilterField 
       { 
        PropertyName = "Schedule_Num", 
        Operator = "IN", 
        Value = new[] {2, 6} 
       }); 

    var result = builder.Build(); 
    return View(result); 
} 

[HttpPost] 
public ActionResult TestPost(QueryResult result) 
{ 
    var builder = new QueryBuilder(_repo, "INFO", date); 

    foreach (var f in result.Filters) 
    { 
     builder.AddFilters(new FilterField 
            { 
             PropertyName = f.PropertyName, 
             Operator = f.Operator, 
             Value = ((Array)f.Value).GetValue(0) 
            }); 
    } 

    builder.AddFilters(
      new FilterField 
       { 
        PropertyName = "Gender", 
        Operator = "BETWEEN", 
        Value = new[] {"A", "G"} 
       }); 

    var newResult = builder.Build(); 
    return View("TestQuery", newResult); 
} 

TestQuery.cshtml

@model Models.QueryResult 

@using (Html.BeginForm("TestPost", "Home")) 
{ 
    @Html.HiddenFor(m => m.Date) 
    for (var i = 0; i < Model.Filters.Count(); i++) 
    { 
     @Html.Hidden("filters[" + i + "].PropertyName", Model.Filters[i].PropertyName) 
     @Html.Hidden("filters[" + i + "].Operator", Model.Filters[i].Operator) 
     @Html.Hidden("filters[" + i + "].Value", Model.Filters[i].Value) 
    } 
    <div class="formArea"> 
     <p> 
      <input type="submit" value="Submit" id="btnSubmit" /> 
     </p> 
    </div> 
} 

QueryResult.cs

public class QueryResult 
{ 
    public DateTime Date { get; set; } 
    public ObjectQuery<EntityObject> Objects { get; set; } 
    public string SqlStatement { get; set; } 
    public ObjectParameter[] Parameters { get; set; } 
    public AdjustResult AdjustResult { get; set; } 
    public IList<FilterField> Filters { get; set; } 

    public QueryResult() 
    { 
     Filters = new List<FilterField>(); 
    } 

    public void AddFilter(FilterField filter) 
    { 
     Filters.Add(filter); 
    } 

    public string ParsedSqlStatement() 
    { 
     var output = Parameters.Aggregate(SqlStatement, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value))); 
     return Filters.Aggregate(output, (current, filter) => current + filter.ConvertToSql()); 
    } 

    private string FormatObjectToString(object value) 
    { 
     if (value is int) 
      return value.ToString(); 
     return String.Format("'{0}'", value); 
    } 
} 

QueryBuilder.cs

public class QueryBuilder 
{ 
    public IList<FilterField> Filters { get; set; } 

    private IDynamicRepository _repo; 
    private string _tablePrefix; 
    private DateTime _date; 
    private QueryResult _base; 

    public QueryBuilder(IDynamicRepository repository, string tablePrefix, DateTime date) 
    { 
     _repo = repository; 
     _tablePrefix = tablePrefix; 
     _date = date; 
     _base = _repo.GetAll(tablePrefix, date); 
     Filters = new List<FilterField>(); 
    } 

    public void AddFilters(params FilterField[] filters) 
    { 
     foreach (var f in filters) 
     { 
      Filters.Add(f); 
     } 
    } 

    public void RemoveFilter(FilterField filter) 
    { 
     Filters.Remove(filter); 
    } 

    public QueryResult Build() 
    { 
     return _base.Where(Filters.ToArray()); 
    } 
} 

Extensions.cs

public static QueryResult Where(this QueryResult result, string predicate, params QueryProperty[] properties) 
{ 
    result.Objects = result.Objects.Where(predicate.ReplaceIdentifier(), properties.Select(p => new ObjectParameter(p.Name, p.Value)).ToArray()); 
    return result; 
} 

public static QueryResult Where(this QueryResult result, FilterField filter) 
{ 
    var filterOutput = filter.GetOutput(); 
    result.Objects = result.Objects.Where(filterOutput.Sql.ReplaceIdentifier(), filterOutput.Properties.Select(p => new ObjectParameter(p.Name, p.Value)).ToArray()); 
    result.AddFilter(filter); 
    return result; 
} 

public static QueryResult Where(this QueryResult result, params FilterField[] filters) 
{ 
    return filters.Aggregate(result, Where); 
} 

詳細情報を知りたい方もいらっしゃいますので、ここですべての関係を詳しくご説明します。基本的に、コントローラーはUIのフィルターのリストを取得して、WHEREの後のSQLステートメントを取得します。したがって、1つのフィルターはFIELD = VALUEまたはFIELD IN(VALUE1、VALUE2)になります。 querybuilderは、Entity.CreateQuery( "SELECT * FROM TABLE")を使用してSQL文のベースを作成します。 querybuilderのメソッドBuild()が実行されると、クエリの日付と、ビューで使用するためにWHERE文に変換された添付ファイルを含むクエリのすべてのEntityObjectsを持つQueryResultモデルが作成されます。私は先に進んで、それらがどのように結びついているかを示すためにいくつかのクラスを追加しました。

+0

TestQueryのモデルとは何ですか?あなたは構造を投稿できますか? – mattytommo

+0

あなたのQueryBuilderは共有できますか?私と比較するといいですね... – Romias

+1

私はオリジナルの質問を編集して、アプリケーションが何をしているのかを示すのに役立ついくつかのコードとともに、必要なコードをすべて含めました。 – snoluet

答えて

5

HiddenForを使用すると、そのような名前を設定する必要はありません。であるためにあなたのビューであなたのforループを変更:あなたのターン中に、デフォルトのモデルバインダーがHttpPost方法:)であなたのQueryResultにフィルタを送って助けるべき正しいマークアップを、与えるべき

for (var i = 0; i < Model.Filters.Count(); i++) 
{ 
    @Html.HiddenFor(m => m.Filters[i].PropertyName) 
    @Html.HiddenFor(m => m.Filters[i].Operator) 
    @Html.HiddenFor(m => m.Filters[i].Value) 
} 

**はEDIT:あなたが複数の値または単一の値を(intまたはstring、どちらかArrayすることができる)結合しているとして、あなたはList<string>するFilterFieldクラスでValueプロパティを変更する最善です。

ので、FilterFieldクラスでこの置き換え:

for (var i = 0; i < Model.Filters.Count(); i++) 
{ 
    @Html.HiddenFor(m => m.Filters[i].PropertyName) 
    @Html.HiddenFor(m => m.Filters[i].Operator) 
    for (var j = 0; j < Model.Filters[i].Values.Count; j++) 
    { 
     @Html.HiddenFor(m => m.Filters[i].Values[j]) 
    } 
} 

その方法、それはかもしれないが:であるためにあなたのマークアップを変更すると

public List<string> Values { get; set; } 

:これにより

public object Value { get; set; } 

を世界で最もきれいなコードのようには見えませんが、それはあなたに邪魔することの頭痛を節約しますあなたがそれを望むように働くモデルバインディングは、デフォルトでバインドするだけです。

+0

残念ながら、私はこのように試してみると同じ結果になります。しかし、それを書いていることの簡略表記に感謝します。 – snoluet

+0

@snoulet問題はありません:)。あなたの「価値」はどのように「オブジェクト」ですか?それが大きな頭痛を引き起こすものです。それは型の間で大きく変わるか、あるいは文字列かintかのどちらかになるでしょうか? – mattytommo

+0

これは、文字列、int、文字列の配列、またはintの配列のいずれかになります。 "IN"節は複数の値を持つことができるので、それに対応するために値の配列を持たせることができなければなりません。 – snoluet

1

私はあなたが考えを適応させることができると思いますfrom this article。あなたのケースで

各項目は、FilterItemsの代わりGiftItems ...デフォルトでは

+1

これは元の質問オブジェクトに配列を渡すことができますが、これは私の次の作業を大きく助けます。入力いただきありがとうございます。私はちょうどUI部分の作業を始めようとしていました。私はこのガイドを使ってそれを手助けします。 – snoluet

+0

あなたのユーザーにフィルタオプションを動的に追加できるようにするといいと思いました。 – Romias

0

なり、同じ名前の複数の入力は、配列に結合します。したがって、各値を離散的に反復し、それを隠し入力(同じ名前)として追加する必要があります。それがポストバックすると、配列としてバインドされます。

各値を反復することで、値として設定されているオブジェクトタイプの問題もなくなります。

+0

これはもっとも洗練された解決策ではありません。しかし、 "Value"の型をオブジェクトから配列*に変更すると、より単純なHtml.HiddenFor(...)が期待どおりに動作するかもしれません。しかし、私はこの観察に100%の信念を持っていません。 – brightgarden

関連する問題