可重用的MVC代码:处理linq表达式

时间:2016-06-28 16:29:07

标签: c# asp.net-mvc linq linq-expressions

概述

我正在编写一个MVC集,旨在为Entity Framework对象提供基本的搜索/添加/编辑/删除功能。它还做了一些奇特的事情,比如支持使用属性从其他表中填充的下拉列表。

正在使用的主要对象是一个可继承的通用控制器(AdminController<T>,其中T是EF对象),包含一个AdminField<T>数组,其中每个元素代表一个字段/属性。 TAdminController<T>可以生成AdminViewModel<T>个对象。下面是这些对象之间关系的Visio图表。

enter image description here

问题

我的问题在于视图。要显示值,我正在使用Html.EditorFor / HiddenFor / TextboxFor助手。超简化视图只是遍历字段并显示其值,如果它们是可编辑字段,则允许编辑。这看起来像:

@model AdminViewModel<ExampleEFObject>
@using (Html.BeginForm())
{
    foreach (var f in Model.Fields)
    {
        var expression = Model.ExpressionFromField(f);

        <label class="control-label col-md-3">@f.DisplayName</label>

        @if (!f.Editable)
        {
            @Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" })
        }
        else
        {
            @Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } })
        }
    }
    <input type="submit" value="Save" class="btn btn-primary" />
}

嗯,这就是我想要它的样子。它适用于作为引用类型的字段/属性。但是,对于值类型,我在System.InvalidOperationException行上出现EditorFor错误:“模板只能用于字段访问,属性访问,单维数组索引或单参数自定义索引器表达式。

这是因为ExpressionFromField方法返回一个访问ViewModel中T实例的表达式,其返回类型为Expression<Func<AdminViewModel<T>, object>>。因为它返回一个对象类型,所以在生成表达式时强制装箱,以便代替(例如)t => t.Count,而我的方法实际上返回t => Convert(t.Count)

当前解决方法

我目前的解决方法是将第二种方法定义为Expression<Func<AdminViewModel<T>, int>> IntExpressionFromField(AdminField<T> field)。因为它返回int,所以它不会强制在表达式中装箱。但是,这让我的观点非常难看:

@model AdminViewModel<ExampleEFObject>
@using (Html.BeginForm())
{
    foreach (var f in Model.Fields)
    {
        var expression = Model.ExpressionFromField(f);
        var intExpression = Model.IntExpressionFromField(f);

        <label class="control-label col-md-3">@f.DisplayName</label>

        @if (!f.Editable)
        {
            if (intExpression == null)
            {
                @Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" })
            }
            else
            {
                @Html.TextBoxFor(intExpression, new { @class = "form-control", @disabled = "disabled" })
            }
        }
        else
        {

            if (intExpression == null)
            {
                @Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } })
            }
            else
            {
                @Html.EditorFor(intExpression, new { htmlAttributes = new { @class = "form-control" } })
            }
        }
    }
    <input type="submit" value="Save" class="btn btn-primary" />
}

更糟糕的是,这只支持1种值类型(int)。我还想支持decimal,long和其他任何东西 - 最好不必为每个方法创建自定义方法以及防止其他类型的所有逻辑。

帮助!

我很想找到一种优雅,简单的方法来解决这个问题。如果不存在,我认为我的下一步将是停止使用Html辅助方法;但是,这需要与应用程序的其余部分打破一致性,我甚至不知道它会解决问题。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

无法使用我现有的方法解决此问题,我尝试了最后提到的方法:放弃HtmlHelper方法。这很有效,我希望我很久以前就完成了。

使用&#34;名称&#34;完成MVC ViewModel绑定。属性,所以要手动绑定到我的模型我需要获取非限定属性名称(即对于表示为cust => cust.Records.Address.State的字段,我需要字符串SelectedItem.Records.Address.State(其中SelectedItem是ViewModel对象包含显示的值。)我的AdminField<T>对象已经有MemberExpression属性,所以我只使用了ToString()方法,删除了参数,并添加了ViewModel对象名称。丑,我知道,但又可靠又容易。

之前,我需要表达式来提供HtmlHelper对象,但现在我只需要该值,因此我使用更简单的ExpressionFromField()方法替换了GetValue()方法,该方法返回{ {1}}价值。它返回string非常重要;此转换允许我读取和写入所有值类型(因为ViewModel绑定处理已编辑值到原始属性类型的转换)。

我的新观点如下:

string

更干净!

这种方法让我失去的一件事就是验证。从这里,我可以添加javascript验证或创建模板来复制@model AdminViewModel<ExampleEFObject> @using (Html.BeginForm()) { foreach (var f in Model.Fields) { var propertyName = f.PropertyName(f); //unqualified, can contain multiple parts var value = Model.GetValue(f); //uses the field expression to retrieve the value //of a "T SelectedItem" object in the ViewModel <label class="control-label col-md-3">@f.DisplayName</label> @if (!f.Editable) { <input class="form-control" disabled="disabled" name="@propertyName" type="text" value="@value" /> } else { <input class="form-control" name="@propertyName" type="text" value="@value" /> } } <input type="submit" value="Save" class="btn btn-primary" /> } 自动添加的数据验证。无论哪种方式,没什么大不了的。

希望这有助于某人。

注意:@ Ivan Stoeve建议使用HtmlHelper / Html.Editor等,它通过属性名称而不是表达式接受绑定。这仍然让我没有在视图中的强类型表达式,但它让我回到验证,所以我认为值得调整它。它还将清除添加下拉选项时我必须做的一些丑陋的字符串操作。 :)