[dynamic] Razor의 동적 익명 유형으로 인해 RuntimeBinderException이 발생 함

다음과 같은 오류가 발생합니다.

‘object’에 ‘RatingName’에 대한 정의가 없습니다.

익명의 동적 유형을 보면 명확하게 RatingName이 있습니다.

오류 스크린 샷

Tuple 로이 작업을 수행 할 수 있다는 것을 알고 있지만 오류 메시지가 발생하는 이유를 알고 싶습니다.



답변

내부 속성을 가진 익명 유형은 .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");
       }
    }
}

이제 익명 개체를 모델로 전달하면 예상대로 작동합니다.