I have a webform where I want to select a list of files, enter some additional informations and submit the whole bundle. The controller should than process the list of files according to the additionally entered datas. Therefore, I want to bundle all in one Model:
public class MyModel
{
public string AdditionalInformations { get; set; }
public int AdditionalInformationsId { get; set; }
public IEnumerable<HttpPostedFile> Files { get; set; }
}
public class MyController: Controller
{
[HttpPost]
public ActionResult UploadFile(MyModel Model)
{
switch(Model.AdditionalInformationsId)
{
case 1:
// do something with Model.Files
case 2:
// do something else with Model.Files
...
}
RedirectToAction("Index");
}
}
My view looks like
#model MyProgram.Models.MyModel
#using MyProgram.Models
#using (Html.BeginForm("UploadFile", "MyController", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<div class="row">
<div class="col-sm-6">
<input type="file" name="Files" multiple />
</div>
<div class="col-sm-6"></div>
</div>
<div class="row">
<div class="col-sm-3">
#Html.DropDownListFor(model => model.AdditionalInformationsId,
((MyDropDownList)Session["DDLists"]).companies,
Model.AdditionalInformation,
new { #class = "form-control", id = "ReceivingCompany" })
</div>
<div class="col-sm-3">
<br />
<button class="btn btn-default" type="submit">Upload</button>
</div>
<div class="col-sm-6"></div>
</div>
}
If I now use the submit-Button, the model is sent to the controller. E.g. AdditionalInformationsId has the value entered in the form. But IEnumerable Files is allways empty, except if I submit the form without choosing any files for upload, in that case the list contains a null-object. Also adding the attribute id="Files" to the multiple-file input-tag does not work.
I can't find the error. Is there maybe a setting I have to set in the web.config?
Change your property type to IEnumerable of HttpPostedFileBase
public IEnumerable<HttpPostedFileBase> Files { get; set; }
Now the posted files will be mapped to the Files property in your HttpPost action.
Related
Very new to MVC Core and C# and just as I think I'm getting the hang of something there's a new curve ball. I have a form which is based on a model which has a foreign key. When I submit the form to the controller the modelState is invalid because the form is passing something back which isn't in the model it is based on. Here is the model:
public partial class Agreement
{
public Agreement()
{
AgreementAmendments = new HashSet<AgreementAmendment>();
Bundles = new HashSet<Bundle>();
Invoices = new HashSet<Invoice>();
}
public int Id { get; set; }
public int OrgId { get; set; }
public string AgreementNumber { get; set; } = null!;
public string? IrespondReference { get; set; }
public string? DocumentLink { get; set; }
public virtual Organization Org { get; set; }
public virtual ICollection<AgreementAmendment> AgreementAmendments { get; set; }
public virtual ICollection<Bundle> Bundles { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
This is the Get Create Action Method:
public IActionResult Create()
{
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName");
return View();
}
This is the form:
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrgId" class="control-label">Organization</label>
<select asp-for="OrgId" class ="form-control" asp-items="ViewBag.OrgId"></select>
</div>
<div class="form-group">
<label asp-for="AgreementNumber" class="control-label">Agreement Number</label>
<input asp-for="AgreementNumber" class="form-control" />
<span asp-validation-for="AgreementNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IrespondReference" class="control-label">Internal Reference</label>
<input asp-for="IrespondReference" class="form-control" />
<span asp-validation-for="IrespondReference" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DocumentLink" class="control-label">Document Link</label>
<input asp-for="DocumentLink" class="form-control" />
<span asp-validation-for="DocumentLink" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
And this is the HttpPost Create Action Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("OrgId,AgreementNumber,IrespondReference,DocumentLink")] Agreement agreement)
{
if (ModelState.IsValid)
{
_context.Add(agreement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "Id", agreement.OrgId);
return View();
}
When I look at the results of the ModelState it shows an error with the Org Key but as far as I can see the form should just be returning the OrgId as per the model. Can someone please let me know where I am going wrong.
Created a View Model for Agreements to handle the form input and then passed that to the base Agreement Model which seems like unnecessary work. Why can't EF Core handle this stuff without having to constantly build View Models just because there is a foreign key?
Anyway, this is the final HttpPost code for others who run into the same issue:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(AgreementWriteViewModel newagreement)
{
if (ModelState.IsValid)
{
var model = new Agreement
{
OrgId = newagreement.OrgId,
AgreementNumber = newagreement.AgreementNumber,
IrespondReference = newagreement.IrespondReference,
DocumentLink = newagreement.DocumentLink,
};
_context.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName", newagreement.OrgId);
return View();
}
I want to upload image in "wwwroot/uploads/img" folder but i get error.I wrote the following code:
Create View :
#model imageuploader.Models.Employee
<form method="post" enctype="multipart/form-data" asp-controller="Employee" asp-action="Create">
<div class="form-group">
<div class="col-md-10">
<input asp-for="FirstName" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input asp-for="LastName" Class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input asp-for="ImageName" type="file" Class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Create" />
</div>
</div>
Model :
public class Employee
{
[Key]
public int ID { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string ImageName { get; set; }
}
Controller
private readonly RegisterDBContext _context;
private readonly IHostingEnvironment _appEnvironment;
public EmployeeController(RegisterDBContext context, IHostingEnvironment appEnvironment)
{
_context = context;
_appEnvironment = appEnvironment;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Employee emp)
{
if (ModelState.IsValid)
{
var files = HttpContext.Request.Form.Files;
foreach (var Image in files)
{
if (Image != null && Image.Length > 0)
{
var file = Image;
//There is an error here
var uploads = Path.Combine(_appEnvironment.WebRootPath, "uploads\\img");
if (file.Length > 0)
{
var fileName = Guid.NewGuid().ToString().Replace("-", "") + Path.GetExtension(file.FileName);
using (var fileStream = new FileStream(Path.Combine(uploads, fileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
emp.BookPic = fileName;
}
}
}
}
_context.Add(emp);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
}
return View(emp);
}
When i click on submit button i get an error (error line is marked), how can i upload image or file in Specified path?
Error:
NullReferenceException: Object reference not set to an instance of an object.
imageuploader.Controllers.EmployeeController+<Create>d__2.MoveNext() in EmployeeController.cs
var uploads = Path.Combine(_appEnvironmen.WebRootPath, "uploads\\img\\");
How can i upload image correctly in Specified path?
I Solved it. I understood i initial bad
_appEnvironment
in Constructor.
With repeated edits, all of the codes in question are currently correct.
Thanks #Shyju user.
Here is how to upload an image in C# Asp.net core Web Application 2.1 MVC:
First I'm assuming you've created a model, added to db and now working in the controller.
I've created a Person model
person model
For create get:
//Get : Person Create
public IActionResult Create()
{
return View();
}
post Action (please see screen shot)
create post action method
Finally the view
Create View
Project Output:
Here is the output of my project
Consider a user can create a Sale advertising (Post model). but every advertising have different properties depend on its Group. Properties are not certain and can be added by admin with different constraints(Required. MinLength etc.)
I define a class like this:
public class Property
{
public int Id { get; set; }
public int Priority { get; set; }
[Required()]
public InputType Type { get; set; }
[Required()]
[MaxLength(150)]
public string Title { get; set; }
[Index(IsUnique=true)]
[Required()]
[MaxLength(100)]
public string Values { get; set; }
[MaxLength(100)]
public string Description { get; set; }
public ICollection<GroupProperty> GroupProperties { get; set; }
public ICollection<PostProperty> PostProperties { get; set; }
}
For example admin can add a model's car property to cars group. after that users must fill a model car field for advertisings in car group.
Create view for advertising is like this:
#model IEnumerable<Property>
<section>
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<h1>New Advertising</h1>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
foreach (var item in Model)
{
#Html.EditorFor(m => item)
}
<button type="submit">hvah</button>
}
</div>
<div class="col-md-6">
</div>
</div>
</div>
</section>
Ah everything goes harder! I have a Editor template for Property class like this:
#model Property
#helper Helper(Property model)
{
switch (model.Type)
{
case WebSite.Models.DomainModels.InputType.NonNegative:
{
<div class="form-group">
<label for="#(model.Name)">#(model.Title)</label>
<span class="field-validation-valid text-danger" data-valmsg-for="#(model.Name)" data-valmsg-replace="true"></span>
<input class="form-control text-box single-line valid" data-val="true"
name="#(model.Name)" type="number" value="0"/>
</div>
return;
}
case WebSite.Models.DomainModels.InputType.RequiredShortString:
{
<div class="form-group">
<label for="#(model.Name)">#(model.Title)</label>
<span class="field-validation-valid text-danger" data-valmsg-for="#(model.Name)" data-valmsg-replace="true"></span>
<input class="form-control text-box single-line" data-val="true"
id="#(model.Name)" name="#(model.Name)" type="text" value="BB"/>
</div>
return;
}
}
}
#Helper(Model)
After all i have Client validation for properties. with hard code i can validate them in server side too. but new problem is Binding! if server side validation goes wrong i need to pass a model to view again. so i am think im doing this with a wrong way. can some one help me? maybe about how solve my problem or a better way to implement this? a simple way to use MVC validation On a complex model like this?
I think you want to create a class and validate ModelState. you can do it like-
Example:
You can pass your model state around like this:
public class MyClass{
public static void errorMessage(ModelStateDictionary ModelState) {
if (something) ModelState.AddModelError("", "Error Message");
}
}
Use in controller:
MyClass.errorMessage(ModelState);
If you need more information about modaestate validation outside then you can fiend more help from this link.
I am having an issue where the default model binder is refusing to bind to my object, which is a List<> of simple objects.
The class:
public class ReferralHistoryDetail {
[Key]
public long Referral_Number { get; set; }
public Guid QuoteGuid { get; set; }
public byte ReferralTypeID { get; set; }
public string Referral_Type { get; set; }
public DateTime ReferralDateTime { get; set; }
public string ReferralComments { get; set; }
}
The controller definition:
[HttpPost]
public ActionResult Save(List<ReferralHistoryDetail> details) {
The view from Fiddler:
What am I doing wrong here?
This is shown via an EditorTemplate in the following manner:
#using (Html.BeginForm("Save", "Home")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="tab-content">
<div class="tab-pane fade in active form-horizontal" id="basic">
#Html.EditorFor(m => m.ReferralHistoryDetail)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
The value that is coming across as NULL is the one being passed to the POST method of the controller. The default model binder is not able to bind this.
I resolved this.
The problem is the Html.BeginForm() is in the main view, but it is only posting back stuff from the EditorFor()'s.
I had to change my controller to take in the main view's object and not the object that the EditorFor() is dealing with, because the Html.BeginForm() lives on the main view.
I want to take some info from my model, edit one variable and pass it to post function. Here is my model:
public class TaskInputModel
{
[Required]
[Display(Name = "Input Value")]
public decimal Value { get; set; }
public long InputId { get; set; }
public MetriceModelTaskShedule[] Tasks;
}
and this is my Index.cshtml:
#model MetriceWeb.Models.TaskInputModel
#foreach (var item in Model.Tasks)
{
using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => item.Task)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Value)
#Html.ValidationMessageFor(model =>model.Value)
</div>
#Html.Hidden("Model.InputId", Model.InputId)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
}
I'm receiving this like that:
[HttpPost]
public ActionResult Index(TaskInputModel model)
{
...
}
When I'm submitting only InputId has some value, Value is always 0. When i delete line: #Html.Hidden("Model.InputId", Model.InputId) Value is ok, but i don't know how to receive InputId. Can you tell me how can I do this?
Problem solved. I just had to use #Html.Hidden("InputId", Model.InputId) instead of #Html.Hidden("Model.InputId", Model.InputId)