0

В моем классе, у меня есть свойство для вложенного файла, как так ...ModelState.IsValid всегда ложно для RegularExpression ValidationAttribute в MVC 4

public class Certificate { 
    [Required] 
    // TODO: Wow looks like there's a problem with using regex in MVC 4, this does not work! 
    [RegularExpression(@"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")] 
    public string AttachmentTrace { get; set; } 
} 

Я не вижу ничего плохого с моим регулярным выражением, но я всегда получаю ModelState.IsValid false. Это кажется довольно тривиальным и простым регулярным выражением, я что-то упускаю? Нужно ли мне писать собственную собственную проверку?

Я заселение AttachmentTrace через обычный вход типа файла:

<div class="editor-label"> 
    @Html.LabelFor(model => model.AttachmentTrace) 
</div> 
<div class="editor-field"> 
    @Html.TextBoxFor(model => model.AttachmentTrace, new { type = "file" }) 
    @Html.ValidationMessageFor(model => model.AttachmentTrace) 
</div> 

метод Действие просто очередная акция:

public ActionResult Create(Certificate certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) 
    { 
     if (ModelState.IsValid) 
     { 
      // code ... 
     } 
     return View(certificate); 
    } 
+0

как вы заселяетесь AttachmentTrace? –

+0

Forty-Two, я заполняю его через обычный ввод файла типа (просто добавленный код выше). Благодарю. – sammydc

+0

Ну, как вы это делаете, я бы предположил, что значение AttachmentTrace не то, что вы ожидаете (имя файла), а скорее нечто вроде «System.Web.HttpPostedFileWrapper». –

ответ

1

Хорошо, вот решение, которое я нашел. Я уверен, что есть другие решения. Сначала немного фона, потому что мое приложение использует EF код-первой миграцию, указав в HttpPostedFileBase типа свойства в моей модели, производит эту ошибку при добавлении миграции:

Один или несколько ошибок проверки были обнаружены в процессе генерации модели: System.Data.Entity.Edm.EdmEntityType:: EntityType «HttpPostedFileBase» не имеет определенного ключа. Определите ключ для этого EntityType. \ tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'HttpPostedFileBases' основан на типе 'HttpPostedFileBase' , который не имеет определенных ключей.

Поэтому мне действительно пришлось придерживаться типа строки для свойства AttachmentTrace.

Решение использовать класс ViewModel, как это:

public class CertificateViewModel { 
    // .. other properties 
    [Required] 
    [FileTypes("xls,xlsx")] 
    public HttpPostedFileBase AttachmentTrace { get; set; } 
} 

Затем создайте FileTypesAttribute как так, я заимствовал этот код из this excellent post.

public class FileTypesAttribute : ValidationAttribute { 
    private readonly List<string> _types; 

    public FileTypesAttribute(string types) { 
     _types = types.Split(',').ToList(); 
    } 

    public override bool IsValid(object value) { 
     if (value == null) return true; 
     var postedFile = value as HttpPostedFileBase; 
     var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1); 
     return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase); 
    } 

    public override string FormatErrorMessage(string name) { 
     return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types)); 
    } 
} 

В контроллере действий, мне нужно внести изменения, чтобы использовать ViewModel вместо, а затем сопоставить его обратно к моей Сущности, используя AutoMapper (которое отлично, кстати):

public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) { 
     if (ModelState.IsValid) { 
      // Let's use AutoMapper to map the ViewModel back to our Certificate Entity 
      // We also need to create a converter for type HttpPostedFileBase -> string 
      Mapper.CreateMap<HttpPostedFileBase, string>().ConvertUsing(new HttpPostedFileBaseTypeConverter()); 
      Mapper.CreateMap<CreateCertificateViewModel, Certificate>(); 
      Certificate myCert = Mapper.Map<CreateCertificateViewModel, Certificate>(certificate); 
      // other code ... 
     } 
     return View(myCert); 
    } 

Для AutoMapper, я создал свой собственный TypeConverter для HttpPostedFileBase следующим образом:

public class HttpPostedFileBaseTypeConverter : ITypeConverter<HttpPostedFileBase, string> { 

    public string Convert(ResolutionContext context) { 
     var fileBase = context.SourceValue as HttpPostedFileBase; 
     if (fileBase != null) { 
      return fileBase.FileName; 
     } 
     return null; 
    } 
} 

Вот и все. Надеюсь, это поможет другим, у кого может быть такая же проблема.