但当查看匿名动态类型时,它显然包含 RatingName。 我知道可以使用 Tuple 解决该问题,但我想了解这个错误消息出现的原因。'object' 不包含 'RatingName' 的定义
但当查看匿名动态类型时,它显然包含 RatingName。 我知道可以使用 Tuple 解决该问题,但我想了解这个错误消息出现的原因。'object' 不包含 'RatingName' 的定义
在我看来,匿名类型具有内部属性是.NET框架设计上的一个糟糕决定。
这里有一个快速而不错的扩展可以解决这个问题,即立即将匿名对象转换为ExpandoObject。
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
使用起来非常简单:
return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando());
当然,在你的视角中:
@foreach (var item in Model) {
<div>x = @item.x, y = @item.y</div>
}
我在相关问题中找到了答案。 答案在David Ebbo的博客文章“将匿名对象传递给MVC视图并使用动态访问它们”中指定。
原因是控制器中传递的匿名类型是内部类型,因此只能从声明它的程序集内部进行访问。由于视图被单独编译,动态绑定器会抱怨无法越过程序集边界。
但是如果您思考一下,这种来自动态绑定器的限制实际上是相当人为的,因为如果您使用私有反射,没有任何东西会阻止您访问那些内部成员(甚至在Medium信任级别下也可以工作)。因此,默认的动态绑定器正在努力执行C#编译规则(其中无法访问内部成员),而不是让您执行CLR运行时允许的操作。
使用 ToExpando 方法是最佳解决方案。
这是一个不需要 System.Web 程序集的版本:
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
{
var obj = propertyDescriptor.GetValue(anonymousObject);
expando.Add(propertyDescriptor.Name, obj);
}
return (ExpandoObject)expando;
}
{ foo: "foo", nestedDynamic: { blah: "blah" } }
- sports不要从匿名类型创建模型,然后尝试像这样将匿名对象转换为ExpandoObject
...
var model = new
{
Profile = profile,
Foo = foo
};
return View(model.ToExpando()); // not a framework method (see other answers)
你可以直接创建ExpandoObject
:
dynamic model = new ExpandoObject();
model.Profile = profile;
model.Foo = foo;
return View(model);
然后在您的视图中,将模型类型设置为动态 @model dynamic
,您可以直接访问属性:
接下来返回:
然后在您的视图中,将模型类型设置为动态 @model dynamic
,您可以直接访问属性:
@Model.Profile.Name
@Model.Foo
通常我会建议为大多数视图使用强类型视图模型,但有时这种灵活性很方便。
IEnumerable<IMadeUpInterface>
,并在Linq的末尾使用.AllActLike<IMadeUpInterface>();
。这是因为它使用DLR调用匿名属性,并使用声明匿名类型的程序集的上下文。编写控制台应用程序并将Mono.Cecil作为参考添加(您现在可以从NuGet中添加),然后编写以下代码:
static void Main(string[] args)
{
var asmFile = args[0];
Console.WriteLine("Making anonymous types public for '{0}'.", asmFile);
var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters
{
ReadSymbols = true
});
var anonymousTypes = asmDef.Modules
.SelectMany(m => m.Types)
.Where(t => t.Name.Contains("<>f__AnonymousType"));
foreach (var type in anonymousTypes)
{
type.IsPublic = true;
}
asmDef.Write(asmFile, new WriterParameters
{
WriteSymbols = true
});
}
以上代码将从输入参数中获取程序集文件,并使用Mono.Cecil将其从internal更改为public,从而解决了问题。
我们可以在网站的Post Build事件中运行该程序。我写了一篇关于此的中文博客文章,但我相信您只需阅读代码和截图即可理解。 :)
根据被接受的答案,我已经在控制器中进行了重写,以使其在一般情况下和后台工作。
这是代码:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
//This is needed to allow the anonymous type as they are intenal to the assembly, while razor compiles .cshtml files into a seperate assembly
if (ViewData != null && ViewData.Model != null && ViewData.Model.GetType().IsNotPublic)
{
try
{
IDictionary<string, object> expando = new ExpandoObject();
(new RouteValueDictionary(ViewData.Model)).ToList().ForEach(item => expando.Add(item));
ViewData.Model = expando;
}
catch
{
throw new Exception("The model provided is not 'public' and therefore not avaialable to the view, and there was no way of handing it over");
}
}
}
public static class impFunctions
{
//converting the anonymous object into an ExpandoObject
public static ExpandoObject ToExpando(this object anonymousObject)
{
//IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
}
在控制器中
public ActionResult VisitCount()
{
dynamic Visitor = db.Visitors
.GroupBy(p => p.NRIC)
.Select(g => new { nric = g.Key, count = g.Count()})
.OrderByDescending(g => g.count)
.AsEnumerable() //important to convert to Enumerable
.Select(c => c.ToExpando()); //convert to ExpandoObject
return View(Visitor);
}
@model IEnumerable<dynamic>
@*@foreach (dynamic item in Model)*@
@foreach (var item in Model)
{
<div>x=@item.nric, y=@item.count</div>
}
使用ExpandoObject扩展是有效的,但在使用嵌套匿名对象时会出现问题。
例如
var projectInfo = new {
Id = proj.Id,
UserName = user.Name
};
var workitem = WorkBL.Get(id);
return View(new
{
Project = projectInfo,
WorkItem = workitem
}.ToExpando());
为了实现这个目标,我使用了这个。
public static class RazorDynamicExtension
{
/// <summary>
/// Dynamic object that we'll utilize to return anonymous type parameters in Views
/// </summary>
public class RazorDynamicObject : DynamicObject
{
internal object Model { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name.ToUpper() == "ANONVALUE")
{
result = Model;
return true;
}
else
{
PropertyInfo propInfo = Model.GetType().GetProperty(binder.Name);
if (propInfo == null)
{
throw new InvalidOperationException(binder.Name);
}
object returnObject = propInfo.GetValue(Model, null);
Type modelType = returnObject.GetType();
if (modelType != null
&& !modelType.IsPublic
&& modelType.BaseType == typeof(Object)
&& modelType.DeclaringType == null)
{
result = new RazorDynamicObject() { Model = returnObject };
}
else
{
result = returnObject;
}
return true;
}
}
}
public static RazorDynamicObject ToRazorDynamic(this object anonymousObject)
{
return new RazorDynamicObject() { Model = anonymousObject };
}
}
在控制器中的用法相同,只需使用 ToRazorDynamic() 而不是 ToExpando()。
在视图中获取整个匿名对象只需在末尾添加 ".AnonValue"。
var project = @(Html.Raw(JsonConvert.SerializeObject(Model.Project.AnonValue)));
var projectName = @Model.Project.Name;
我要从https://dev59.com/e2s05IYBdhLWcg3wDttn#7478600 偷一点东西
如果你安装 dynamitey 包,你可以这样做:
return View(Build<ExpandoObject>.NewObject(RatingName: name, Comment: comment));
而农民们欢呼雀跃。
.Select
使用预定义的投影,会怎么样?例如:someLinq.Select(preDefinedProjection().ToExpando())
,其中preDefinedProjection()
返回一个Expression<Func<SomeType, dynamic>>
。 - sportsdynamic
,例如:public class Model { public IEnumerable<ExpandoObject> Property { get; set; }
然后在视图中:@foreach (
dynamicitem in Model.Property) @item.anonPropName
。 - ngless