Dictionary<TEntity, string> and asp.net mvc how to? - c#

I have:
public class Nomenclature
{
public virtual int NomenclatureId { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
public virtual IDictionary<NomenclatureAttribute, string> Attributes { get; set; }
}
public class NomenclatureType
{
public virtual int NomenclatureTypeId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Nomenclature> Nomenclatures { get; set; }
public virtual ICollection<NomenclatureAttribute> NomenclatureAttributes { get; set; }
public NomenclatureType()
{
Nomenclatures = new HashSet<Nomenclature>();
NomenclatureAttributes = new HashSet<NomenclatureAttribute>();
}
}
public class NomenclatureAttribute
{
public virtual int NomenclatureAttributeId { get; set; }
public virtual string AttributeName { get; set; }
public virtual string AttributeType { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
}
it's all represents a nomenclature of goods in my application. I'am tryin create new Nomenclature in my app. I use NHibernate. I create controller and create action:
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
ViewData["NomenclatureAttributes"] =
_repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList();
return View();
}
[HttpPost]
public IActionResult Create(Nomenclature nomenclature)
{
try
{
if (ModelState.IsValid)
{
_repositoryNomenclature.Create(nomenclature);
return RedirectToAction("List", "Nomenclature");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists see your system administrator.");
}
return View(nomenclature);
}
I need to foreach all NomenclatureAttrributes specified for any Nomenclature Type and create editors for all values and add all to Model.Attributes.
#model Nomenclature
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#foreach (var a in (List<NomenclatureAttribute>)ViewData["NomenclatureAttributes"])
{
<div class="form-group">
<label class="control-label col-md-2">#a.AttributeName</label>
<div class="col-md-10">
**What i should place to this???**
</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>
}
I use Asp.net core web application (.NET Framework)

First Create ViewModel.
public class CreateNomenclatureViewModel
{
//Add other properties if needed
public NomenclatureType SelectedNomenclatureType { get; set; }
public List<NomenclatureAttribute> Attributes { get; set;}
}
Second
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
var viewModel= new CreateMomenClatureViewModel
{
Attributes = _repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList()
}
return View(viewModel);
}
Than fix your view
#model CreateNomenclatureViewModel
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#if (Model != null && Model.Attributes != null)
{
for (int i = 0; i < Model.Attributes.Count; i++)
{
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes [i].AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes [i].AttributeType )
</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>
}
if you want to use Nomenclature as ViewModel you can create new Nomenclature on Get Method than pass to view in razor view.
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeType )
</div>

Related

How to save changes to a view model in C#?

I am currently reading values in a configuration file and setting the values to a view model. I am displaying them on the UI in textboxes. I want the user to be able to edit/change the value in the textbox and be able to hit a save button and for the changes to be saved to the view model and the configuration file. I understand there needs to be some type of Get/Post method in the controller but I'm not entirely sure how the controller should look. I am not connecting it to a database at all.
View:
#using (Html.BeginForm())
{
<fieldset>
<div class="row">
<div class="col-md-1">Logging Directory:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.loggingDirectory)</div>
<div class="col-md-1">Archive Directory:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.archiveDirectory)</div>
<div class="col-md-1">Time Between Alarms:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.timeBetweenAlarms)</div>
<div class="col-md-1">Time to Archive Logs:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.timeToArchive)</div>
<div class="col-md-1">Situator IP:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.situatorIP)</div>
<div class="col-md-1 ">Situator Port:</div>
<div class="col-md-2 ">#Html.EditorFor(model => Model.situatorPort)</div>
<div class="col-md-1 ">Clean Up:</div>
<div class="col-md-2 ">#Html.EditorFor(model => Model.timeToCleanUp)</div>
<div class="col-md-1 ">Coorelation Zone:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.coorelationZone)</div>
</div>
<div class="row submitButton">
<button class="btn btn-primary" type="submit">Save</button>
</div>
</fieldset>
}
View Model
public class ConfigurationViewModel
{
public string loggingDirectory { get; set; }
public string archiveDirectory { get; set; }
public string situatorIP { get; set; }
public string situatorPort { get; set; }
public string timeBetweenAlarms { get; set; }
public string timeToArchive { get; set; }
public string sightlogixIP { get; set; }
public string timeToCleanUp { get; set; }
public string coorelationZone { get; set; }
}
Controller:
public ActionResult Index()
{
ConfigurationViewModel cvm = new ConfigurationViewModel();
cvm.loggingDirectory = ConfigurationManager.AppSettings["loggingDirectoryPath"];
cvm.archiveDirectory = ConfigurationManager.AppSettings["archiveDirectoryPath"];
cvm.situatorIP = ConfigurationManager.AppSettings["SituatorIP"];
cvm.situatorPort = ConfigurationManager.AppSettings["SituatorPort"];
cvm.timeBetweenAlarms = ConfigurationManager.AppSettings["TimeIncrementBetweenalarmsInSeconds"];
cvm.timeToArchive = ConfigurationManager.AppSettings["timeIncrementForArchivingLogFilesInHours"];
cvm.sightlogixIP = ConfigurationManager.AppSettings["SightLogixIP"];
cvm.timeToCleanUp = ConfigurationManager.AppSettings["timeIncrementForCleaningUp"];
cvm.coorelationZone = ConfigurationManager.AppSettings["correlationZoneLengthInFeet"];
return View(cvm);
}
[HttpGet]
public ActionResult Edit()
{
return;
}
[HttpPost]
public ActionResult Edit()
{
return;
}
Pass view model in Get Edit method
[HttpGet]
public ActionResult Edit()
{
ConfigurationViewModel cvm = new ConfigurationViewModel();
cvm.loggingDirectory = ConfigurationManager.AppSettings["loggingDirectoryPath"];
cvm.archiveDirectory = ConfigurationManager.AppSettings["archiveDirectoryPath"];
cvm.situatorIP = ConfigurationManager.AppSettings["SituatorIP"];
cvm.situatorPort = ConfigurationManager.AppSettings["SituatorPort"];
//...
return View(cvm);
}
Send updated view model to post edit method and perform action on it
[HttpPost]
public ActionResult Edit(ConfigurationViewModel cvm)
{
ConfigurationManager.AppSettings["archiveDirectoryPath"] = cvm.archiveDirectory;
ConfigurationManager.AppSettings["SituatorIP"] = cvm.situatorIP;
ConfigurationManager.AppSettings["SituatorPort"]= cvm.situatorPort;
//...
return View(cvm);
}
And your razor view which will submit updated data to your Post Edit method
#using (Html.BeginForm("Edit", "Your controller", FormMethod.Post))
{
....
}

Binding partial to multiple actionresults

I was wondering if anybody could suggest a way of overcoming the following issue. I have the following models
// AModel
public class AModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class AModelViewModel
{
public AModelViewModel()
{
AModel = new AModel();
}
public AModel AModel { get; set; }
}
//BModel
public class BModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class BModelViewModel
{
public BModelViewModel()
{
BModel = new BModel();
}
public BModel BModel { get; set; }
}
This is what my controller looks like
public ActionResult PageA()
{
var model = new AModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageA(AModel aModel)
{
return View();
}
public ActionResult PageB()
{
var model = new BModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageB(BModel bModel)
{
return View();
}
The two views look like this
//PageA
#model WebApplication13.Models.AModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
//PageB
#model WebApplication13.Models.BModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
Both views use the following partial view
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
<input type="text" class="form-control" name="Keyword" />
</div>
The problem I have is that because this partial is shared, and the name attribute of the keyword input is 'Keyword', when I submit the form on either page the keyword property is never binded so it's always null. Is there a way I can share this partial, but alter the prefix depending on what page i'm using. Any help would be greatly appreciated.
Instead of using a paritial view.. Create an EditorTemplate in Views\Shared\EditorTemplates named Keyword.cshtml with code something like
#model string
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
#Hmlt.TextBoxFor(a => a, new {class="form-control"})
</div>
then in your views you just call
#Html.EditorFor(a => a.AModel.Keyword,"Keyword")
if you have to use PartialView.. you can pass in an HtmlFieldPrefix via ViewDataDictionary when you call the partial view. To do this, you would call your partial like this.
#Html.Partial("~/Views/Shared/_RandomView.cshtml",
new ViewDataDictionary { TemplateInfo = new TemplateInfo() {
HtmlFieldPrefix = "AModel" } });
then in your partial view you will need to use the #Html.Textbox helper like so.
<div class="form-group">
<label for="keyword">Keyword</label>
#Html.TextBox("Keyword", string.Empty, new { #class="form-control" })
</div>

c# ASP.NET MVC cannot pass model's collection field to controller [duplicate]

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 7 years ago.
I have a class from EF.
public partial class Customer
{
public Customer()
{
this.Customer_CustomerSpecialConcern = new HashSet<Customer_CustomerSpecialConcern>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public virtual ICollection<Customer_CustomerSpecialConcern> Customer_CustomerSpecialConcern { get; set; }
}
When I pass the model from controller to view everything works fine (can access Customer_CustomerSpecialConcern values).
The problem is when I post back model to a controller to save the changes the property Customer_CustomerSpecialConcern is null.
Here is how I use it in view.
#foreach (var ccsc in Model.Customer_CustomerSpecialConcern)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for="">#ccsc.CustomerSpecialConcern.Title</label>
<div class="col-md-1 field-input">
#Html.EditorFor(model => ccsc.Value)
#Html.HiddenFor(model => ccsc.Value)
</div>
</div>
}
Please, I need help to get the values of this collection property to controller. Thank you.
Update - Customer_CustomerSpecialConcern class details
public partial class Customer_CustomerSpecialConcern
{
public int Id { get; set; }
public int Customer_Id { get; set; }
public int CustomerSpecialConcern_Id { get; set; }
public bool Value { get; set; }
public virtual Customer Customer { get; set; }
public virtual CustomerSpecialConcern CustomerSpecialConcern { get; set; }
}
Please try this,
#for (int i = 0; i < Model.Customer_CustomerSpecialConcern.Count(); i++)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for="">#Model.CustomerSpecialConcern[i].Title</label>
<div class="col-md-1 field-input">
#Html.EditorFor(model => model.Customer_CustomerSpecialConcern[i].Value)
#Html.HiddenFor(model => model.Customer_CustomerSpecialConcern[i].Value)
</div>
</div>
}
Check this article.
I tried your example, and this is how it looks
public ActionResult Index()
{
var customer = new Customer
{
Name = "Name",
Surname = "Surname",
Email = "email#email.com",
Mobile = "mobile...",
Customer_CustomerSpecialConcern = new List<Customer_CustomerSpecialConcern>
{
new Customer_CustomerSpecialConcern
{
Value = true
},
new Customer_CustomerSpecialConcern
{
Value = true
}
}
};
return View(customer);
}
View:
#model WebApplication1.Models.Customer
#{
ViewBag.Title = "Customer";
var customer_CustomerSpecialConcern = Model.Customer_CustomerSpecialConcern.ToList();
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
for (int i = 0; i < Model.Customer_CustomerSpecialConcern.Count(); i++)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for=""></label>
<div class="col-md-1 field-input">
#Html.CheckBoxFor(model => customer_CustomerSpecialConcern[i].Value)
</div>
</div>
}
<input type="submit" value="Save"/>
}

MVC 5 not validating StringLength attribute properly

I'm trying to validate the sortCode field in my PersonPaymentDetails model but my view is failing to validate the StringLength(6). If I submit the form with a value of length 1 it incorrectly validates successfully.
Am I doing something fundamentally wrong here?
/* [CONTROLLER] */
public class PersonController : Controller
{
[HttpGet]
[Route("person/paymentDetails/create/{personId?}")]
public ActionResult PaymentDetailsCreate(int? personId)
{
if (personId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = db.People.Find(personId);
if (person == null)
{
return HttpNotFound();
}
PersonPaymentDetailsViewModel personPaymentDetailsVM = new PersonPaymentDetailsViewModel();
personPaymentDetailsVM.SetPerson(person);
return View(personPaymentDetailsVM);
}
[HttpPost]
[Route("person/paymentDetails/create")]
public ActionResult PaymentDetailsCreate(PersonPaymentDetailsViewModel personPaymentDetailsVM)
{
if (ModelState.IsValid)
{
/* should not be entering here with sortCode = 123, as not 6 characters in length */
return Content("No errors: |" + personPaymentDetailsVM.SinglePaymentDetails.sortCode + "|");
}
}
}
/* [VIEW] */
#model X.ViewModels.PersonPaymentDetailsViewModel
#Html.ValidationSummary()
#using (Html.BeginForm("PaymentDetailsCreate", "Person", FormMethod.Post, new { #class = " form-horizontal" }))
{
#Html.HiddenFor(m => m.Person.id, "default")
<div class="form-group">
<label for="bankSortCode" class="col-md-3 control-label">Sort Code</label>
<div class="col-md-9">
#Html.EditorFor(m => m.SinglePaymentDetails.sortCode, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<label for="save" class="col-md-3 control-label"> </label>
<div class="col-md-9">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
}
/* [MODEL] */
public partial class PersonPaymentDetails
{
public int id { get; set; }
[Required, StringLength(6)]
public string sortCode { get; set; }
}
/* [ViewModel] */
public class PersonPaymentDetailsViewModel
{
public Person Person { get; set; }
public PersonPaymentDetails SinglePaymentDetails { get; set; }
public void SetPerson(Person person)
{
this.Person = person;
this.SinglePaymentDetails = new PersonPaymentDetails();
}
}
You want
[Required, StringLength(6, MinimumLength = 6)]
Constructor of StringLength takes in the maximum length only, so as you currently have it it checks that string is not longer than 6 characters, and therefore string of length 1 passes validation successfully.

Model (complex type) won't submit to action

I have some problem posting a form with 'complex type' model:
I have a Model:
public class CircleEditViewModel
{
[Key]
public int CircleId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public bool IsSystem { get; set; }
public class UserInCircle
{
public UserInCircle(User user)
{
this.UserId = user.UserId;
FullName = user.FullName;
}
public int UserId { get; set; }
public byte[] Picture { get; set; }
public string FullName { get; set; }
public bool isInCircle { get; set; }
}
public List<UserInCircle> Users { get; set; }
}
My first problem was that at post event, my Users where null.. so i followed a few posts on here (like MVC- Model Binding on a Complex Type) to use a for instead of a foreach,but since i did so, my form won't post anymore:
View:
#model Wims.Website.ViewModels.CircleEditViewModel
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse('form');
});
</script>
#using (Ajax.BeginForm(Html.ViewContext.RouteData.Values["Action"].ToString(), null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SaveDone(data)" }, new { id = "editform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Circle</legend>
#Html.Label(DateTime.Now.ToString());
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
if (Model.Users != null)
{
for (int i = 0; i < Model.Users.Count; i++)
{
<div class="userDetail">
<div>
<div>
#Html.DisplayFor(model => Model.Users[i].isInCircle);
</div>
<div class="iconDiv">
#Html.Image("~/Content/Images/defaultUser.jpg", Model.Users[i].FullName, null);
</div>
<div>
#Html.TextBoxFor(model => Model.Users[i].FullName)
#Html.HiddenFor(model => Model.Users[i].UserId)
</div>
</div>
</div>
<div style="clear: both"></div>
}
}
#Html.GenerateSecureDataControls(model => model.CircleId)
<input type="submit" value="Save" />
}
My view is rendered as a partial loaded thru ajax (not sure it makes any difference here).
Any idea why it won't post? If i remove all the '[]' like 'Users[0].FullName' to Users0.FullName i will post, but of course it won't be bound.
Thanks for your help
Edit just in case needed: Action:
[HttpPost]
public ActionResult Edit(CircleEditViewModel circleData, FormCollection collection)
{
if (ModelState.IsValid)
{
using (var logic = new CircleLogic())
{
Circle circle = logic.GetCircleById(circleData.CircleId, WebMatrix.WebData.WebSecurity.CurrentUserId);
if (circle == null)
{
return HttpNotFound();
}
else
{
circle.Name = circleData.Name;
logic.UpdateCircle(circle, GetSelectedUser(collection));
}
return PartialView("_CircleAndUsers", GetData(logic, circle.CircleId));
}
}
return this.Json(new { success = false, viewdata = RenderRazorViewToString("_CircleAndUsers", circleData) });
}
Pablo Romeo was right, i added a default ctor and it worked.

Categories

Resources