2

最後に別のステートメントを追加する必要がある場合に、MVC コントローラーに戻す必要があるクエリ ステートメントのリストがあります。今作成しようとしているテストでは、ページはフィルターのリストから始まります。この例を実行すると、ページはフィルター用のフィールドを次のように作成します。

<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

Value パラメーターを配列としてキャストし、最初のインデックスを取得して値を取得できるようにする必要があります。これは問題ありませんが、理想的ではありません。主な問題は、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() が実行されると、クエリの日付とクエリのすべての EntityObjects を持つ QueryResult モデルが作成され、フィルターが添付されて、ビューで使用するための WHERE ステートメントに変換されます。私は先に進み、いくつかのクラスを追加して、それらがどのように結びついているかを示しました。

4

3 に答える 3

5

あなたの見解では、あなたのためHiddenForにそれを行うために使用できるので、そのような名前を設定する必要はありません。ビューの for ループを次のように変更します。

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)
}

これにより、正しいマークアップが得られるはずです。これにより、デフォルトのモデルバインダーがメソッドでフィルターを送信するのに役立ちますQueryResult:) HttpPost

**編集: 複数の値または単一の値 (intまたはstring、いずれかが an である可能性があります) をバインドしているため、クラスのプロパティをaArrayに変更するのが最善です。ValueFilterFieldList<string>

したがって、FilterFieldクラスでこれを置き換えます。

public object Value { get; set; }

これとともに:

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

次に、マークアップを次のように変更します。

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])
    }
}

そうすれば、世界で最も美しいコードのようには見えないかもしれませんが、モデルバインディングをハックして希望どおりに動作させる必要があるという頭痛の種から解放されます。デフォルトでバインドするだけです。

于 2012-05-01T16:14:47.153 に答える
1

この記事のアイデアを適用できると思います。

ケース内の各アイテムは、GiftItems ではなく FilterItems になります...

于 2012-05-01T16:15:47.043 に答える
0

デフォルトでは、同じ名前の複数の入力が配列にバインドされます。したがって、各値を個別に繰り返し処理し、非表示の入力として (同じ名前で) 追加する必要があります。ポストバックすると、配列としてバインドされます。

各値を反復処理すると、オブジェクト タイプが値として設定される問題も解消されます。

于 2012-05-01T17:32:30.127 に答える