답변:
내부 속성을 가진 익명 유형은 .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보기로 전달하고 동적을 사용하여 액세스
그 이유는 익명 형식이 컨트롤러의 내부에 전달되므로 선언 된 어셈블리 내에서만 액세스 할 수 있기 때문입니다. 뷰가 개별적으로 컴파일되므로 동적 바인더는 해당 어셈블리 경계를 넘을 수 없다고 불평합니다.
그러나 당신이 그것에 대해 생각하면, 동적 바인더의 이러한 제한은 실제로 매우 인공적입니다. 개인 반사를 사용하면 그 내부 구성원에 액세스하는 것을 막을 수있는 것은 없습니다 (그렇습니다, 심지어 중간 신뢰에서도 작동합니다). 따라서 기본 동적 바인더는 CLR 런타임에서 허용하는 작업을 수행하는 대신 C # 컴파일 규칙 (내부 멤버에 액세스 할 수없는 위치)을 적용하지 못하고 있습니다.
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;
}
익명 형식에서 모델을 만든 다음 익명 개체를 다음 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.Profile.Name
@Model.Foo
일반적으로 대부분의 뷰에 대해 강력한 형식의 뷰 모델을 권장하지만 때로는 이러한 유연성이 유용합니다.
프레임 워크 즉석 인터페이스 를 사용하여 인터페이스 에서 익명 유형을 래핑 할 수 있습니다 .
IEnumerable<IMadeUpInterface>
Linq 사용을 반환 .AllActLike<IMadeUpInterface>();
하면 익명 유형을 선언 한 어셈블리 컨텍스트와 함께 DLR을 사용하여 익명 속성을 호출하기 때문에 Linq 사용 이 끝납니다 .
콘솔 응용 프로그램을 작성하고 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을 사용하여 액세스 가능성을 내부에서 공개로 변경하면 문제가 해결됩니다.
웹 사이트의 빌드 후 이벤트에서 프로그램을 실행할 수 있습니다. 나는 이것에 관한 블로그 게시물을 중국어로 썼지 만 코드와 스냅 샷을 읽을 수 있다고 생각합니다. :)
허용 된 답변을 바탕으로 컨트롤러에서 일반적인 상황과 뒤에서 작동하도록 재정의했습니다.
코드는 다음과 같습니다.
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");
}
}
}
이제 익명 개체를 모델로 전달하면 예상대로 작동합니다.
https://stackoverflow.com/a/7478600/37055 에서 약간의 도둑질을 할 것입니다.
패키지 다이나마이트 를 설치 하면 다음을 수행 할 수 있습니다.
return View(Build<ExpandoObject>.NewObject(RatingName: name, Comment: comment));
그리고 농민들은 기뻐합니다.
RuntimeBinderException이 발생하는 이유는 다른 게시물에 좋은 대답이 있다고 생각합니다. 나는 실제로 어떻게 작동하는지 설명하는 데 집중합니다.
ASP.NET MVC의 Anonymous Type 컬렉션을 사용하여 @DotNetWise 및 바인딩 뷰 를 참조하십시오 .
먼저 확장을위한 정적 클래스 만들기
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 뷰 (모델 클래스가 아닌 동적)에서는 익명 유형 객체를 바인딩 할 때 매우 중요합니다.
@model IEnumerable<dynamic>
@*@foreach (dynamic item in Model)*@
@foreach (var item in Model)
{
<div>x=@item.nric, y=@item.count</div>
}
foreach의 유형으로 var 또는 dynamic을 사용하는 동안 오류가 없습니다 .
그건 그렇고, 새로운 필드와 일치하는 새로운 ViewModel을 생성하면 결과를 뷰에 전달할 수 있습니다.
이제 재귀 맛
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value == null || new[]
{
typeof (Enum),
typeof (String),
typeof (Char),
typeof (Guid),
typeof (Boolean),
typeof (Byte),
typeof (Int16),
typeof (Int32),
typeof (Int64),
typeof (Single),
typeof (Double),
typeof (Decimal),
typeof (SByte),
typeof (UInt16),
typeof (UInt32),
typeof (UInt64),
typeof (DateTime),
typeof (DateTimeOffset),
typeof (TimeSpan),
}.Any(oo => oo.IsInstanceOfType(o.Value))
? o.Value
: o.Value.ToExpando()));
return (ExpandoObject) expandoObject;
}
ExpandoObject Extension을 사용하면 중첩 된 익명 객체를 사용할 때 작동하지만 중단됩니다.
와 같은
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 };
}
}
컨트롤러의 사용법은 ToExpando () 대신 ToRazorDynamic ()을 사용하는 것을 제외하고 동일합니다.
전체 익명 객체를 얻으려면 끝에 ".AnonValue"를 추가하면됩니다.
var project = @(Html.Raw(JsonConvert.SerializeObject(Model.Project.AnonValue)));
var projectName = @Model.Project.Name;
ExpandoObject를 시도했지만 다음과 같이 중첩 된 익명의 복잡한 유형에서는 작동하지 않았습니다.
var model = new { value = 1, child = new { value = 2 } };
그래서 내 솔루션은 JObject를 View 모델로 반환하는 것이 었습니다.
return View(JObject.FromObject(model));
.cshtml에서 동적으로 변환하십시오.
@using Newtonsoft.Json.Linq;
@model JObject
@{
dynamic model = (dynamic)Model;
}
<span>Value of child is: @model.child.value</span>