简体   繁体   English

无法将字节设为必填字段

[英]Cannot make Byte a Required Field

Have an app where I would like to upload an image. 有一个我想上传图像的应用程序。 I am using byte array for this. 我为此使用字节数组。

The image is required but when I put that annotation on the variable and try create an image, it gives me the error message when it should have the image there. 该图像是必需的,但是当我将该注释放在变量上并尝试创建图像时,当该图像应具有该图像时,它会给我错误消息。 It also returns that the Model is Invalid. 它还返回模型无效。

When I remove the Required, it works but it can also be set as null which is what I don't want. 当我删除Required时,它可以工作,但也可以将其设置为null,这是我不想要的。

There doesn't seem to be a lot on the topic on Stack Overflow. 关于堆栈溢出的话题似乎并不多。

Here is my Model 这是我的模特

[Key]
public int InvoiceId { get; set; }

[Required(ErrorMessage = "Company is required")]
public string Company { get; set; }

[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }

[Required(ErrorMessage = "Amount is required")]
public decimal Amount { get; set; }

[Required(ErrorMessage = "Picture of Invoice Required")]
public byte[] PictureOfInvoice { get; set; }

And my controller: 而我的控制器:

[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create([Bind(Include = "InvoiceId,Company,Description,Amount,PictureOfInvoice,DateReceived,ChurchId")] Invoice invoice,HttpPostedFileBase File)
{
    if (ModelState.IsValid)
    {
        if (File != null && File.ContentLength > 0)
        {                   
            invoice.PictureOfInvoice = new byte[File.ContentLength];
            File.InputStream.Read(invoice.PictureOfInvoice, 0, File.ContentLength);

        }
        else
        {
            TempData["Error"] = "Upload an Image";
        }

        db.Invoices.Add(invoice);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    ViewBag.ChurchId = new SelectList(db.Churches, "ChurchId", "Name", invoice.ChurchId);
    return View(invoice);
}

My View just in case its something there: 我的视图以防万一

<h2>Add New Invoice</h2>

@if (TempData["Error"] != null)
{
    <div style="color:red">@TempData["Error"]</div>
}

@using (Html.BeginForm("Create", "Invoices", FormMethod.Post, new { @class = "form-horizontal", enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary()

    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Company, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Company, new { htmlAttributes = new { @class = "form-control", style = "width:20em;" } })
                @Html.ValidationMessageFor(model => model.Company, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Amount, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Amount,new { htmlAttributes = new { @class = "form-control", style = "width:20em;" } })
                @Html.ValidationMessageFor(model => model.Amount, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.DateReceived, "DateRecieved", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.DateReceived, new { htmlAttributes = new { @class = "form-control", style = "width:20em;" } })
                @Html.ValidationMessageFor(model => model.DateReceived, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Description, new { htmlAttributes = new { @class = "form-control", style = "width:20em;" } })
                @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.PictureOfInvoice, "Picture of Invoice", htmlAttributes: new { @class = "control-label col-md-2"})
            <div class="col-md-10">
                <input type="file" name="File" id="File" style="width: 50%;" />
                @Html.ValidationMessageFor(model => model.PictureOfInvoice, "", new { @class = "text-danger" })
                <output id="list"></output>
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.ChurchId, "Church Name", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("ChurchId", null, htmlAttributes: new { @class = "form-control", style = "width:20em;" })
                @Html.ValidationMessageFor(model => model.ChurchId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    <u>
        @Html.ActionLink("Back to List", "Index")
    </u>
</div>


<script src="~/Scripts/jquery.datetimepicker.js"></script>

<script>
    $('#DateReceived').datetimepicker({
        format: 'd/m/Y',
        weeks: true,
        disableWeekDays: [0, 1, 3, 4, 5, 6],
        timepicker: false,
        inline: false
    });

    function handleFileSelect(evt) {
        var files = evt.target.files; // FileList object

        // Loop through the FileList and render image files as thumbnails.
        for (var i = 0, f; f = files[i]; i++) {

            // Only process image files.
            if (!f.type.match('image.*')) {
                continue;
            }

            var reader = new FileReader();

            // Closure to capture the file information.
            reader.onload = (function (theFile) {
                return function (e) {
                    // Render thumbnail.
                    var span = document.createElement('span');
                    span.innerHTML = ['<img class="thumb" src="', e.target.result,
                        '" title="', escape(theFile.name), '"/>'
                    ].join('');
                    document.getElementById('list').insertBefore(span, null);
                };
            })(f);

            // Read in the image file as a data URL.
            reader.readAsDataURL(f);
        }
    }

    document.getElementById('File').addEventListener('change', handleFileSelect, false);
</script>

The mixing of concerns here is cause problems. 这里混合的关注是导致问题的原因。 It appears you are trying to use an entity as the model for the view and have it satisfy both UI validation and persistence validation. 看来您正在尝试将实体用作视图的模型,并使其同时满足UI验证和持久性验证。

Separate the two concerns. 将两个问题分开。

Create a view model specific to the desired behavior of the view. 创建特定于视图所需行为的视图模型。 The model should also include IEnumerable<SelectListItem> ChurchList property to populate the drop down. 该模型还应包括IEnumerable<SelectListItem> ChurchList属性以填充下拉列表。

public class CreateInvoiceViewModel {

    [Required(ErrorMessage = "Company is required")]
    public string Company { get; set; }

    [Required(ErrorMessage = "Description is required")]
    public string Description { get; set; }

    [Required(ErrorMessage = "Amount is required")]
    public decimal Amount { get; set; }

    [Required(ErrorMessage = "Picture of Invoice Required")]
    public HttpPostedFileBase File { get; set; }

    public int ChurchId { get; set; }

    public IEnumerable<SelectListItem> ChurchList { get; set; }

    //...other properties
}

and set that as the model of the view 并将其设置为视图的模型

@model CreateInvoiceViewModel

If creating a new invoice there is no id assigned as yet. 如果创建新发票,则尚未分配ID。 That means when posting the model as you current have it, the model state cannot be valid as InvoiceId , which is tagged as Required is not provided. 这意味着当发布当前模型时,模型状态不能有效,因为InvoiceId不会被标记为Required

The uploaded file (invoice picture) should also be included in the view model and should use @Html.TextBoxFor(m => m.File, new { type = "file" }) to get client side validation. 上传的文件(发票图片)也应包含在视图模型中,并应使用@Html.TextBoxFor(m => m.File, new { type = "file" })获得客户端验证。 The model binder will set that property based on whether a matching input was provided. 模型绑定器将根据是否提供匹配的输入来设置该属性。

<div class="form-group">
    @Html.LabelFor(model => model.File, "Picture of Invoice", htmlAttributes: new { @class = "control-label col-md-2"})
    <div class="col-md-10">            
        @Html.TextBoxFor(model => model.File, new { type = "file", style = "width: 50%;"})
        @Html.ValidationMessageFor(model => model.File, "", new { @class = "text-danger" })
        <output id="list"></output>
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.ChurchId, "Church Name", htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.ChurchId, (IEnumerable<SelectListItem>)@Model.ChurchList, htmlAttributes: new { @class = "form-control", style = "width:20em;" })
        @Html.ValidationMessageFor(model => model.ChurchId, "", new { @class = "text-danger" })
    </div>
</div>

So now on the controller side, [Bind] should be removed as it's not needed when using a view model. 因此,现在在控制器端,应删除[Bind]因为在使用视图模型时不需要使用[Bind]

[HttpGet]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create() {
    var model = new CreateInvoiceViewModel {
        //set default property values as needed
    };

    model.ChurchList = new SelectList(db.Churches, "ChurchId", "Name");

    //...

    return View(model);
}

[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create(CreateInvoiceViewModel model) {
    if (ModelState.IsValid) {
        var file = model.File;
        //copy properties over to entity
        var invoice = new Invoice {
            Company = model.Company,
            Description = model.Description,
            Amount = model.Amount,
            DateReceived = model.DateReceived,
            ChurchId = model.ChurchId,
            //create array for file contents
            PictureOfInvoice = new byte[file.ContentLength]
        };
        //populate byte array
        file.InputStream.Read(invoice.PictureOfInvoice, 0, file.ContentLength);

        db.Invoices.Add(invoice);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    //if we get this far model state is invalid.
    //return view with validation errors.
    model.ChurchList = new SelectList(db.Churches, "ChurchId", "Name", model.ChurchId);
    return View(model);
}

The model state will provide the necessary feedback if the model requirements are not valid. 如果模型要求无效,则模型状态将提供必要的反馈。 Removing the need for using the temp data and the file check. 无需使用临时数据和文件检查。

You're not using data binding to populate PictureOfInvoice in your model - you're doing it inside your controller's method. 您没有在模型中使用数据绑定来填充PictureOfInvoice ,而是在控制器的方法中进行的。 But you're only performing that step after confirming that the model is valid, which it cannot be by this logic. 但是,只有确认模型有效之后才执行该步骤,而根据此逻辑,模型将无效。

I think you can try just switching the first bits around: 我认为您可以尝试仅切换前几位:

[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create(
[Bind(Include = "InvoiceId,Company,Description,Amount,DateReceived,ChurchId")]
     Invoice invoice,
     HttpPostedFileBase File)
{
    if (invoice!=null && File != null && File.ContentLength > 0)
    {                   
        invoice.PictureOfInvoice = new byte[File.ContentLength];
        File.InputStream.Read(invoice.PictureOfInvoice, 0, File.ContentLength);
    }
    if (ModelState.IsValid)
    {
        //Proceed

If that's still not letting the model pass validation then you may have to just continue applying errors as you previously were in the else 如果仍然不能让模型通过验证,那么您可能必须像以前在else继续应用错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM