2014-06-16 1 views
2

뷰 모델 유형의 일반 인수를 사용하는 정적 일반 FormBuilder HTML 도우미 메서드 (HTMLHelper 클래스의 확장 메서드)가 있으며 데이터베이스에서 하나 이상의 문자열 속성 이름이 전달되면 .NET 4.5를 사용하여 ASP.NET MVC 5.1에서 HTML 양식을 생성합니다.반환 형식 람다 식을 동적으로 표현

양식을 생성하는 공용 메서드가 하나 있고 양식 내의 "모듈"섹션을 생성하기 위해 개별 메서드를 분리 한 다음 각 필드를 그 안에 각각 렌더링합니다. 타입 인자는이 체인을 위에서 아래로 전달됩니다. 은 "RenderField"방법에서

나는 사용하여 입력 된 Html 헬퍼를 작성 도우미가 RenderForm 방법으로 확장 Html 헬퍼 인 코드 -

var typedHelper = helper as HtmlHelper<TModel>; 

.

그때 이것은 내가 나중에이 문자열 편집기 작동 follows-

fieldContainer.InnerHtml += typedHelper.EditorFor(expression); 
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(expression); 

같은 EditorFor, DisplayFor, 또는 ValidationMessageFor을 만드는 데 사용할 수있는 코드 -

var modelType = typeof(TModel); 

... 

var modelProperty = modelType.GetProperty(field.PropertyName); 

if (modelProperty == null) 
{ 
    Elmah.ErrorSignal.FromCurrentContext().Raise(new ArgumentException(string.Format("Model {0} does not contain property {1}", modelType.Name, field.PropertyName))); 
    return null; 
} 

var modelPropertyType = modelProperty.PropertyType; 

var parameter = Expression.Parameter(modelType, "m"); 

var property = Expression.Property(parameter, field.PropertyName); 

var expression = Expression.Lambda<Func<TModel, object>>(property, parameter); 

사용하여 수식을 만듭니다 ,하지만 nullable datetime을 시도하면 오류가 발생합니다.

형식이 'System.Nullable`1 [S ystem.DateTime] ' 는 반환 형식을 사용할 수 없습니다'으로 System.Object '

나는 시도하고 다음과 같은 라인 -

을 변경하여 내가 존 소총 답변에서 보았 듯이 객체에 속성을 변환하면
var expression = Expression.Lambda<Func<TModel, object>>(Expression.Convert(property, typeof(object)), parameter); 

그리고 편집자 코드 변경 -

var compiledExpression = expression.Compile()(model); 

fieldContainer.InnerHtml += typedHelper.EditorFor(compiledExpression); 
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(compiledExpression); 

내가 유형의 인수가 사용에서 유추 할 수없는 오류 메시지를.

반환 유형을 "동적"으로 변경하면 "확장 메서드를 동적으로 디스패치 할 수 없습니다"라는 메시지가 표시됩니다.

제네릭 메서드의 반환 인수로 "modelPropertyType"을 지정할 수 없습니다. 아마도 컴파일 타임에 구체 형식이 될 수있는 것은 아니기 때문일 수 있습니다.

ASP.NET MVC에서 제공하는 EditorFor 도우미 메서드를 사용할 수 있도록 런타임에 속성의 반환 형식을 동적으로 지정할 수있는 방법이 있습니까?

답변

3

TModelTProperty에 대한 일반적인 인수를 사용하여 도우미 함수를 만들면 단일 모델 속성을 편집하기 위해 html을 빌드 할 수 있습니다. 이 도우미는 HtmlHelper<TModel>PropertyInfo의 인스턴스를 받게되며 EditorFor 등의 올바른 람다식이 만들어집니다. 도우미는 컨테이너 사업부 <div class="editor-container"></div> 생성

private static MvcHtmlString GetPropertyEditor<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, PropertyInfo propertyInfo) 
{ 
    //Get property lambda expression like "m => m.Property" 
    var modelType = typeof(TModel); 
    var parameter = Expression.Parameter(modelType, "m"); 
    var property = Expression.Property(parameter, propertyInfo.Name); 
    var propertyExpression = Expression.Lambda<Func<TModel, TProperty>>(property, parameter); 

    //Get html string with label, editor and validation message 
    var editorContainer = new TagBuilder("div"); 
    editorContainer.AddCssClass("editor-container"); 
    editorContainer.InnerHtml += htmlHelper.LabelFor(propertyExpression); 
    editorContainer.InnerHtml += htmlHelper.EditorFor(propertyExpression); 
    editorContainer.InnerHtml += htmlHelper.ValidationMessageFor(propertyExpression); 
    return new MvcHtmlString(editorContainer.ToString()); 
} 

그건, 그 내부 HTML 라벨, 편집기 및 유효성 검사 메시지가 포함됩니다 도우미는 다음과 같이 보일 수 있습니다.

위에서 볼 수 있듯이 도우미는 일반 속성 유형 TProperty을 제공해야합니다.이 속성은 리플렉션을 사용하여 각 속성을 반복 할 때 필요하지 않습니다. 당신은 또한 각 속성에 대한이 도우미를 호출하는 반사를 사용할 수 있습니다 그러나 :

public static MvcHtmlString RenderForm<TModel>(this HtmlHelper<TModel> htmlHelper) 
{ 
    var modelType = typeof(TModel); 
    var form = new TagBuilder("form"); 
    foreach (var propertyInfo in modelType.GetProperties()) 
    { 
     //call generic GetPropertyEditor<TModel, TProperty> with the type of this property 
     var openMethod = typeof(HtmlExtensions).GetMethod("GetPropertyEditor", BindingFlags.Static | BindingFlags.NonPublic); 
     var genericMethod = openMethod.MakeGenericMethod(modelType, propertyInfo.PropertyType); 
     var editorHtml = genericMethod.Invoke(null, new object[] { htmlHelper, propertyInfo }); 
     //add the html to the form 
     form.InnerHtml += editorHtml; 
    } 

    return new MvcHtmlString(form.ToString()); 
} 

모델 등을 감안할 때 :

foreach (var propertyInfo in modelType.GetProperties()) 
{ 
    var openMethod = typeof(HtmlExtensions).GetMethod("GetPropertyEditor", BindingFlags.Static | BindingFlags.NonPublic); 
    var genericMethod = openMethod.MakeGenericMethod(modelType, propertyInfo.PropertyType); 
    var editorHtml = genericMethod.Invoke(null, new object[] { htmlHelper, propertyInfo }); 
    //add editorHtml to the form 

} 

그래서 당신은 주어진 모델에 대한 양식을 생성하고 자신의 HtmlHelper 확장 메서드를 만들 수 있습니다 다음

public class RegisterViewModel 
{ 
    [Required] 
    [Display(Name = "User name")] 
    public string UserName { get; set; } 

    [Required] 
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 
    [DataType(DataType.Password)] 
    [Display(Name = "Password")] 
    public string Password { get; set; } 

    [DataType(DataType.Password)] 
    [Display(Name = "Confirm password")] 
    [System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 
    public string ConfirmPassword { get; set; } 

    public DateTime? RegisterDate { get; set; } 
} 

당신은 다음과 같이 뷰에서 사용할 수 있습니다 :

@Html.RenderForm() 

그리고 다음과 같은 HTML을 생성합니다 : 당신은 생성 된 HTML의 구조, 클래스 이름, 속성 등을위한 다른 요구 사항이있을 것이다

<form> 
    <div class="editor-container"> 
     <label for="UserName">User name</label> 
     <input class="text-box single-line" data-val="true" data-val-required="The User name field is required." id="UserName" name="UserName" type="text" value="" /><span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span> 
    </div> 
    <div class="editor-container"> 
     <label for="Password">Password</label> 
     <input class="text-box single-line password" data-val="true" data-val-length="The Password must be at least 6 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Password" name="Password" type="password" value="" /><span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span> 
    </div> 
    <div class="editor-container"> 
     <label for="ConfirmPassword">Confirm password</label> 
     <input class="text-box single-line password" data-val="true" data-val-equalto="&#39;Confirm password&#39; and &#39;Password&#39; do not match." data-val-equalto-other="*.Password" id="ConfirmPassword" name="ConfirmPassword" type="password" value="" /><span class="field-validation-valid" data-valmsg-for="ConfirmPassword" data-valmsg-replace="true"></span> 
    </div> 
    <div class="editor-container"> 
     <label for="RegisterDate">RegisterDate</label> 
     <input class="text-box single-line" data-val="true" data-val-date="The field RegisterDate must be a date." id="RegisterDate" name="RegisterDate" type="datetime" value="" /><span class="field-validation-valid" data-valmsg-for="RegisterDate" data-valmsg-replace="true"></span> 
    </div> 
</form> 

을,하지만이 당신이했던 FormBuilder을 완료하면 도움이 될 수 있습니다 희망 쓰기!

+0

마지막으로 제출 한 코드는 우리 사양의 복잡성으로 인해 솔루션 전체에 표면적 인 유사점을 지니지 만 ** 사용자의 호출 및 MakeGenericMethod 코드에 의해 완전히 뒷받침됩니다 **. 당신에게 감사의 커다란 빚을지고 제가 할 수 있다면 여러 번 upvote 할 것입니다. 며칠 동안 현상금을 낼 수는 없지만 가능한 한 빨리 상금을 수여 할 것입니다. – pwdst

+0

걱정하지 마세요. @pwdst, 도움이 되었기 때문에 기쁩니다! (예, HTML을 생성하여 양식을 작성하고 양식의 전체 예제를 생성했지만 아이디어가 명확 해지기를 바랍니다 :)) –

+0

Bounty는 실제로 24 시간 동안 수여 할 수는 없지만 발행했습니다. . – pwdst

관련 문제