1

我不认为这会那么难,但我试图将一个具有动态属性的类(当前在 c# 中设置为 expando 对象)传递到视图中。在这个视图中,使用一些 jQuery 模板渲染了一个很好的部分,我想如果我在 c# 代码中有这个:

public dynamic SomeProperty {get;set;}
...
SomeProperty = new ExpandoObject();
SomeProperty.SomeValue = "5";
return View(TheClassThatContainsSomeProperty);

这样在 jquery 模板中我可以这样做:

${SomeProperty.SomeValue}

...并且希望它会起作用。它没有......如果您检查响应,您可以看到它实际上是作为字典发送的:

SomeProperty: [{SomeValue, Value:5}]
0: {Key:SomeValue, Value:5}

这导致(我猜)我的下一个问题:是否有一种简单的方法可以访问 jquery 模板中的字典?我确实试过这个:

${SomeProperty["SomeValue"]}

也无济于事。在这一点上,我唯一知道要做的就是利用在模板中放置函数的能力(从jquery网站复制到这里):

模板:

<tr><td>${getLanguages(Languages, " - ")}</td></tr>

代码:

function getLanguages( data, separator ) {
    return data.join( separator );
}

那么我是否过度复杂化了呢?我是否可以轻松地 1)从 jquery 模板访问动态值或 2)轻松地从 jquery 模板中的字典中查找值?

4

4 回答 4

1

ExpandoObject派生自IEnumerable<KeyValuePair<string, Object>>,并且当您分配 时,大多数序列化程序会将 a 识别dynamic为这种类型ExpandoObject这就是为什么您会在 javascript 端看到一个带有命名键::对的数组类型。

ExpandoObject 类 (System.Dynamic) @ MSDN

使用的一种替代方法ExpandoObject是使用 C# 匿名类型。当序列化为 json 时,这些映射字段按您的预期。

匿名类型(C# 编程指南)@ MSDN

可以从 jQuery 访问使用声明的值dynamic,但很可能您不会返回View()带有要使用 jQuery 使用的模型的 MVC,因为任何服务器端视图模板引擎(剃须刀等)已经可以执行相同的操作具有较少开销的模板活动。相反,jQuery 模板更适合与 Ajax 调用一起使用。

下面的代码示例演示了三种情况,其中dynamic服务器上声明的变量在浏览器中与 jQuery 模板一起使用。

第一个示例对成员字段使用匿名类型SomeValue,并有一个将其视为成员对象的 jQuery 模板。

第二个示例对成员字段使用匿名类型数组,SomeValue并具有使用{{each}}语法枚举项目的模板。请注意,在这种情况下,事情可能会变得很糟糕dynamic,因为您没有得到强类型支持,并且必须知道正确的类型或在您访问它时发现它。

第三个示例使用ExpandoObject成员字段SomeValue,并具有与第一个示例类似的 jQuery 模板(单个成员对象)。请注意,在这种情况下,我们需要使用辅助函数pivotDictionaryMap()Key :: Value对转换为对象成员。

从一个空白的 C# MVC3 Web 项目开始,我们需要修改三个文件来演示这些示例。

_Layout.cshtml中,为 jQuery 模板添加脚本,并在 <head> 元素中添加适当版本的 jQuery。

<script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.tmpl.js")" type="text/javascript"></script>

HomeController.cs中,我们将添加一些返回 jsonActionResult的方法。另外,为简洁起见,我们只SomeModelType在这里声明一个类;请注意,一个适当的应用程序可能会在其Models中声明此类。

using System.Dynamic; // up top...

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";

        return View();
    }

    public ActionResult SomeDataSource(int id)
    {
        dynamic d = new { innerId = 99, innerLabel = "inside object" };
        SomeModelType obj = new SomeModelType(id, "new object");
        obj.SomeValue = d;

        return Json(obj, "text/plain");
    }

    public ActionResult SomeDataSourceWithArray(int id)
    {
        dynamic d1 = new { innerId = 99, innerLabel = "inside object (first array member)" };
        dynamic d2 = new { innerId = 100, innerLabel = "inside object (second array member)" };

        SomeModelType obj = new SomeModelType(id, "new object");
        obj.SomeValue = new object[] {d1, d2};

        return Json(obj, "text/plain");
    }

    public ActionResult SomeDataSourceWithExpando(int id)
    {
        dynamic d = new ExpandoObject();
        d.innerId = 99;
        d.innerLabel = "inside object";

        SomeModelType obj = new SomeModelType(id, "new object");
        obj.SomeValue = d;

        return Json(obj, "text/plain");
    }
}

public class SomeModelType
{
    public SomeModelType(int initId, string initLabel)
    {
        Id = initId;
        Label = initLabel;
    }

    public int Id { get; set; }
    public string Label { get; set; }
    public dynamic SomeValue { get; set; }
}

最后,在默认视图中,我们将为模板添加脚本标签以及使用它们所需的 javascript。注意$.post()and not的使用$.get(),因为JsonResultMVC 中的 a 默认不允许 GET 请求(您可以使用属性打开这些请求)。

@{
    ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>

<script id="someDataTemplate" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has an inner item with id <b>${SomeValue.innerId}</b> whose label is "<i>${SomeValue.innerLabel}</i>".
</script>

<h3>SomeDataSource Example #1 (Single Item)</h3>
<div id="someData">
</div>

<script id="someDataArrayTemplate" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has these inner items:
<ul>
 {{each SomeValue}}
    <li><b>${innerId}</b> has a label "<i>${innerLabel}</i>".</li>
 {{/each}}
</ul>
</script>

<h3>SomeDataSource Example #2 (Array)</h3>
<div id="someArrayData">
</div>

<script id="someDataTemplateFromExpandoObject" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has an inner item with id <b>${SomeValue.innerId}</b> whose label is "<i>${SomeValue.innerLabel}</i>".
</script>

<h3>SomeDataSource Example #3 (Single Item, Expando Object)</h3>
<div id="someDataFromExpandoObject">
</div>

<script type="text/javascript">
function pivotDictionaryMap(src)
{
    var retval = {};

    $.each(src, function(index, item){
        retval[item.Key] = item.Value;
    });

    return retval;
}
</script>

<script type="text/javascript">
$(document).ready(function() {
    // Ajax Round-Trip to fill example #1
    $.post("/Home/SomeDataSource/5", function(data) {
        $("#someDataTemplate").tmpl(data).appendTo("#someData");
    }, "json");

    // Ajax Round-Trip to fill example #2
    $.post("/Home/SomeDataSourceWithArray/67", function(data) {
        $("#someDataArrayTemplate").tmpl(data).appendTo("#someArrayData");
    }, "json");

    // Ajax Round-Trip to fill example #3
    $.post("/Home/SomeDataSourceWithExpando/33", function(data) {
        data.SomeValue = pivotDictionaryMap(data.SomeValue);
        $("#someDataTemplateFromExpandoObject").tmpl(data).appendTo("#someDataFromExpandoObject");
    }, "json");
});
</script>
于 2012-11-20T22:25:07.447 回答
0

不确定这是否有帮助,但看看这个要点。很难从您的代码片段中分辨出来,但如果您要将其ExpandoObject转换为 JSON,请尝试将其包装在第一个上DynamicJsonObject,就像在 gist 中所做的那样。


Gist 中的代码复制/粘贴给那些不想点击链接的人:

// By default, Json.Encode will turn an ExpandoObject into an array of items,
// because internally an ExpandoObject extends IEnumerable<KeyValuePair<string, object>>.
// You can turn it into a non-list JSON string by first wrapping it in a DynamicJsonObject.
[TestMethod]
public void JsonEncodeAnExpandoObjectByWrappingItInADynamicJsonObject()
{
    dynamic expando = new ExpandoObject();
    expando.Value = 10;
    expando.Product = "Apples";

    var expandoResult = System.Web.Helpers.Json.Encode(expando);
    var dictionaryResult = System.Web.Helpers.Json.Encode(new DynamicJsonObject(expando));

    Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", expandoResult); // FAILS (encodes as an array instead)
    Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", dictionaryResult); // PASSES
}
于 2012-11-20T23:08:03.453 回答
0

仅在我不喜欢这个答案的前提下,我不会将自己的答案标记为“正确”。如果有人弄清楚我在上面试图做什么,我很乐意给你积分。

    function getDisplayValue(data, toMatchOn) {
        var _return = $.grep(data, function (n, i) { return (n.Key == toMatchOn); })[0];

        if (_return != null)
            return _return.Value;

        return "";
    }

并在 jquery 模板中:

${getDisplayValue(DisplayFields, 'Something')}

所以我能够让它与上述方法一起工作,所以这是一个可能的解决方案,但我不太了解javascript,无法知道这可能会造成多么糟糕的性能影响(当我研究它时,我会更新这个答案)但我认为 javascript 字典已针对其键值进行了优化,因此 MVC 没有将 expando 序列化为真正的 javascript 字典这一事实似乎使这个答案非常低效。事实上,我最初采用动态 c# 对象的方法是,我最初认为这会序列化为更清晰的形式。无论如何,这行得通,但奥卡姆剃刀只是让这种感觉太复杂了。

于 2012-11-20T22:24:00.813 回答
0

你是对的,如果你遍历一个数组来找到一个键,性能将会很粗略。但是您应该能够轻松地将键值对数组(服务器发回的)转换为“真正的”Javascript 字典/映射。例如:

var kvps = [ {key: "test", value: "expando"}, {key: "hello", value: "world" } ]; 
var map = {};
kvps.forEach(function(kvp) { map[kvp.key] = kvp.value; } );
console.log( JSON.stringify(map) );

{“测试”:“扩展”,“你好”:“世界”}

当然,如果您是嵌套对象,那么您必须对上述方法应用递归才能使其工作。

于 2012-11-21T18:07:56.273 回答