I have only taken a small draft from the side,
it is such that I must have sent some data in my database,
what I've read me to is that such textbox must have the same name throughout from the controller has and model,
index.cshtml
#Html.TextBox("Email", null, new
{
#class = "form-control input-lg",
placeholder = "Skriv email her",
type = "email"
})
Kontocontroller.cs
[HttpPost]
public ActionResult CreateUsers(OpretBrugere brugere)
{
if (ModelState.IsValid)
{
//Is that the right way that I must find out something with database or should I do it differently?
var email = Request["Email"].ToString();
var CreateAnAccount = db.Users.FirstOrDefault(b => b.Email == email);
if(CreateAnAccount != null)
{
brugere.Email = email;
}
}
}
on my model have i opretbrugere.cs
public class OpretBrugere
{
public int UserId { get; set; }
public string Email { get; set; }
}
What do I wrong såden that it will not go into the database, as I said, I have much more to model and cshtml ago to be entered into the database, but I will make a brief description of the task that make it easy for you.
It could be better if you use MVC's model binder. Do something like this:
In your view add your model OpretBrugere as view model
#model Mynamespace.OpretBrugere
and then use html helper to generate proper input like this:
#html.EditorFor(m=>m.Email)
then in your controller you could read data from action's parameter:
[HttpPost]
public ActionResult CreateUsers(OpretBrugere brugere)
{
if (ModelState.IsValid)
{
var email = brugere.Email; // user's submitted data is here
// save to db or whatover you want
}
}
Related
I am in need of how the correct way to do this.
I can not use forms authentication
A user will "login" or confirm identity based on a value
I need to walk the user through a series of pages like so
Contact/MailAddress
Contact/Phone
Contact/Email
Contact/Summary
Questionaire/Question1
Questionaire/Question2
Questionaire/Question3
Questionaire/Summary
Final/Certify
Final/Review
I plan on using Session to hold the data but I'm having trouble figuring out how to pass the values to other views and how Redirect to other pages.
Any help will do...
Lets say you have some models like this
public class ContactModel
{
public string MailAddress { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
public class QuestionaireModel
{
public string Question1Answer { get; set; }
public string Question2Answer { get; set; }
public string Question3Answer { get; set; }
}
public class ContactQuestionaireModel
{
public ContactModel Contact { get; set; }
public QuestionaireModel Question { get; set; }
}
and you want to persist this model from view to view and action to action. In you controller you can create 2 actions. one for your first view and one for your second
Controller
public ActionResult ContactAddress()
{
var model = new ContactQuestionaireModel();
return View(model);
}
[HttpPost]
public ActionResult ContactAddress(ContactQuestionaireModel model)
{
var currentModel = TempData["currentModel"] as ContactQuestionaireModel;
currentModel.Contact.MailAddress = model.Contact.MailAddress;
TempData["currentModel"] = currentModel;
return RedirectToAction("ContactPhone");
}
public ActionResult ContactPhone()
{
var model = TempData["currentModel"] as ContactQuestionaireModel;
return View(model);
}
[HttpPost]
public ActionResult ContactPhone(ContactQuestionaireModel model)
{
var currentModel = TempData["currentModel"] as ContactQuestionaireModel;
currentModel.Contact.Phone = model.Contact.Phone;
TempData["currentModel"] = currentModel;
return RedirectToAction("ContactEmail");
}
in the first action ContactAddress you create a new blank model and pass that in to your view ContactAddress. In that view you can set TempData["currentModel"] equal to the model you are passing in. This will stay in TempData for 1 post back to the server. You dont need to do this on the first page since it's blank anyway but i'm doing it to save time.
View ContactAddress
#model WebApplication3.Models.ContactQuestionaireModel
#{
ViewBag.Title = "Contact Address";
TempData["currentModel"] = Model; //this will be available to me in the HttpPost action
}
#using (Html.BeginForm())
{
<div class="form-group">
#Html.LabelFor(m => m.Contact.MailAddress, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Contact.MailAddress, new { #class = "form-control" })
</div>
</div>
<button type="submit">Submit</button>
}
you'll notice in the controller code that the Post Action for ContactAddress is setting a var currentModel equal to what is in TempData["currentModel"] which was set in the ContactAddress view. Before you do a redirect to the next action ContactPhone set TempData["currentModel"] back to the model you are building and use it in the next action.
You do have the option of adding the Model as a parameter to each action and passing the currentModel object like
public ActionResult ContactPhone(ContactQuestionaireModel model)
return RedirectToAction("ContactPhone", currentModel);
its up to you really. this is not a foolproof way. page refreshes and back and forward buttons could clear out everything that was entered. Storing the information in Session or actually saving the data in a database might be more optimal.
I advise against doing what you are attempting to do by logging in with session but what you are looking for is:
TempData.yourModel = new SomeModel { Data = "yourData" };
//next page
var model = (SomeModel)TempData.yourModel;
and
RedirectToAction("yourController", "yourAction");
I have a view that is using a model and I am using that information to create a form.
I have three steps of the form that are optional or may not be shown.
The problem is that these hidden sections get posted along with the form data and break the business logic. (I have no control over the business logic)
So is there a way to tell the framework not to pass certain sections or fields? Perhaps VIA a class or something?
I know I could use AJAX to send certain sections as they are needed, but the site spec is to have them hidden and displayed as needed.
Although you could do this client-side, it won't stop malicious over-posting/mass assignment.
I suggest reading 6 Ways To Avoid Mass Assignment in ASP.NET MVC.
Excerpts:
Specify Included Properties only:
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
Specify Excluded Properties only:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Use TryUpdateModel()
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel(user, includeProperties: new[] { "FirstName" });
// ...
}
Using an Interface
public interface IUserInputModel
{
string FirstName { get; set; }
}
public class User : IUserInputModel
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel<IUserInputModel>(user);
// ...
}
Use the ReadOnlyAttribute
public class User
{
public string FirstName { get; set; }
[ReadOnly(true)]
public bool IsAdmin { get; set; }
}
Lastly, and the most recommended approach is to use a real ViewModel, instead a domain Model:
public class UserInputViewModel
{
public string FirstName { get; set; }
}
Show/Hide will not allow/disallow the value from being sent to the Controller.
Elements that are Disabled or just not editable will (99% of the time) be returned as null / minVal.
You can set the elements in the View as Disabled by using JQuery in the script:
$('#elementID').attr("disabled", true);
OR you could use a DOM command:
document.getElementById('elementID').disabled = "true";
So you can set the fields as both Disabled AND Hidden, so that it is neither displayed, nor populated. Then in your Controller you can just base the Business Logic on whether or not certain fields (preferable Mandatory fields, if you have any) are null.
You can check this in C# like this:
For a string:
if (string.IsNullOrWhiteSpace(Model.stringField))
{
ModelState.AddModelError("stringField", "This is an error.");
}
For a DateTime:
if (Model.dateTimeField == DateTime.MinValue)
{
ModelState.AddModelError("dateTimeField ", "This is an error.");
}
Just for interest sake, here is how you can Hide/Show elements on the View using JQuery:
$('#elementID').hide();
$('#elementID').show();
I am using C#, MVC3, EF5, SQL Server 2008 R2.
I have an intersection table ie
Lecturer -< LecturerCourse >- Course
The list of Lecturers are populated.
When I add a course, it would be neat to have a list of Lecturers that I could select from, that teach the course in question. When I save the new Course record, this multiselect also should save its data back to the "LecturerCourse" table via Model Binding.
I am using EF5.
Can you recommended a simple and standard approach to solving CRUD for a join, ie "LecturerCourse", table? I have looked online, but some of the approaches seem very complicated.
Many thanks.
Alright, it's going to be a long one. To allow this to happen in "one page" (through POST, or you could use Ajax, technically), you need a combination of a Get and Post version of the method and to construct your view model correctly. Below are the classes that I will use for demonstration purposes:
public class NewCourse
{
[Required]
public string Name { get; set; }
// And your other properties
public int[] LecturerIds { get; set; }
}
public class ViewLecturer
{
public int Id { get; set; }
public int Name { get; set; }
}
public class NewCourseViewModel
{
public NewCourse Course { get; set; }
public IEnumerable<ViewLecturer> Lecturers { get; set; }
}
NewCourseViewModel will be the model for the View (see below). ViewLecturer will give you a lighter mapping between your available Lecturer and the information required to Add to them.
As for the Controller:
public class CourseController : Controller, IDisposable
{
private Lazy<YourContext> lazyContext =
new Lazy<YourContext>(() => new YourContext());
private YourContext Context
{
get { return lazyContext.Value; }
}
public ActionResult New()
{
var model = new NewCourseViewModel {
Course = new NewCourse(),
Lecturers = Context.Lecturers
.Select(l => new ViewLecturer { Id = l.Id, Name = l.Name })
};
return View(model);
}
[HttpPost]
public ActionResult New(NewCourse course)
{
if(ModelState.IsValid)
{
var lecturers = course.Lecturers
.Select(l => new Lecturer { Id = l.Id })
.ToList();
foreach(var lecturer in lecturers)
Context.Lecturers.Attach(lecturer);
var newCourse = new Course {
Name = course.Name,
// ... and the rest of the mapping
Lecturer = lecturers
};
context.Courses.Add(newCourse);
context.SaveChanges();
// Could have to handle DbUpdateException if you want
return RedirectToAction(...);
}
return View(new NewCourseViewModel {
Course = course,
Lecturers = Context.Lecturers
.Select(l => new ViewLecturer { Id = l.Id, Name = l.Name })
});
}
public void Dispose()
{
if(lazyContext.IsValueCreated)
lazyContext.Value.Dispose();
}
}
Your first New method will give you the entry point for your Course creation page. The rest of the validation and actual adding will be done through the [HttpPost]overload. As for your View (that should be in the ~/Views/Course/New.cshtml):
#model NewCourseViewModel
// ... Then when you are ready to begin the form
#using(Html.BeginForm("New", "Course", FormMethod.Post))
{
// Your List of Lecturers
#Html.ListBoxFor(m => m.Course.LecturerIds,
new MultiSelectList(
Model.Lecturers,
"Id",
"Name",
m.Course.LecturerIds ?? new int[0]
))
// Your Other Model binding
}
When the submit button will be pressed, the action matched will be the New(NewCourse course). The names are important because of the way the HtmlHelpers generate their Ids. Because we are only included one property of the whole view model, it will match the parameter name course based on the view model's Course property. You will get a list of Ids for the Lecturers which you will be able to use to attach to the DbContext and add directly to the new Course model (Entity Framework will do the rest). In cases where there was a problem, we can get back the list of lecturers and re-use the same NewCourse in the view model.
Now this is example is very basic but it should give you a good starting point as to how you can structure your view model.
Issuer has as a related field KeyEntity. My code works, but I think that there must be a better way to update the related field other than loading entities from database.
There should be something wrong in ViewModeling and templating... Thanks in advance.
OpViewModel
public class OpViewModel
{
public Connect.Models.Issuer Issuer { get; set; }
public IEnumerable<SelectListItem> KeyEntities { get; set; }
public OpViewModel() { }
public OpViewModel(Connect.Models.Issuer issuer,
IEnumerable<Jose.Models.KeyEntity> key_entities)
{
this.Issuer = issuer;
this.KeyEntities = key_entities.Select(k =>
new SelectListItem()
{
Selected = (k.Id == issuer.KeyEntity.Id),
Text = k.Id.ToString(),
Value = k.Id.ToString()
});
}
}
Op/Edit.chtml(Razor)
#model AdConnect.Models.OpViewModel
#using (Html.BeginForm()) {
<div class="editor-field">
#Html.DropDownListFor(model => model.Issuer.KeyEntity.Id,
Model.KeyEntities)
</div>
}
OpController
[HttpPost]
public ActionResult Edit(Models.OpViewModel obj)
{
if (ModelState.IsValid)
{
// This works for most data fields of Issuer, but KeyEntity's Id
// (dbo.Issuers.KeyEntity_Id) is not updated.
//
// ctx.Entry(obj.Issuer).State = System.Data.EntityState.Modified
// So load Issuer and KeyEntity from the database and update.
var issuer = ctx.Issuers.Find(obj.Issuer.Id);
if (obj.Issuer.KeyEntity != null)
{
issuer.KeyEntity =
ctx.KeyEntities.Find(obj.Issuer.KeyEntity.Id);
}
ctx.Entry(issuer).CurrentValues.SetValues(obj.Issuer);
ctx.SaveChanges();
return RedirectToAction("Index");
}
return View(obj);
}
I think you are missing a bit around how navigation properties work. EF can automatically populate them so you aren't doing it in the constructor of the object.
Have a read of my article on nav properties here.
Also in terms of your controller update code the normal path for an update like this is: Load, Modify, Save.
ie
var issuer = ctx.Issuers.Include(i=>i.KeyEntity).SingleOrDefault(i=>i.Id == obj.Issuer.Id);
issuer.Whatever = obj.Issuer.Whatever; //note prehaps you want something like CopyMatchinProperties here
ctx.SaveChanges();
This is what my data model classes look like:
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Position Position { get; set; }
}
public class Position
{
public string Title { get; set; }
}
I have a Create view where I want to have two text boxes for first name and last name, and then a dropdown box that has the position title. I tried doing it this way:
View (only the relevant part):
<p>
<label for="Position">Position:</label>
<%= Html.DropDownList("Positions") %>
</p>
Controller:
//
// GET: /Employees/Create
public ActionResult Create()
{
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
return View();
}
//
// POST: /Employees/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd)
{
try
{
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
_employees.AddEmployee(employeeToAdd);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However, when I click submit, I get this exception:
System.InvalidOperationException was unhandled by user code
Message="There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Positions'."
I'm pretty sure I'm doing this wrong. What is the correct way of populating the dropdown box?
You can store:
(string)ViewData["Positions"]};
in a hiddn tag on the page then call it like this
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd, string Positions)
{
In the Create() (WITH POST ATTRIBUTE) employee since the ViewData["Positions"] is not set you are getting this error. This value should form part of your post request and on rebinding after post should fetch it from store or get it from session/cache if you need to rebind this..
Remember ViewData is only available for the current request, so for post request ViewData["Positions"] is not yet created and hence this exception.
You can do one quick test... override the OnActionExecuting method of the controller and put the logic to fetch positions there so that its always availlable. This should be done for data that is required for each action... This is only for test purpose in this case...
// add the required namespace for the model...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// add your logic to populate positions here...
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
}
There may be other clean solutions to this as well probably using a custom model binder...
I believe that ViewData is for passing information to your View, but it doesn't work in reverse. That is, ViewData won't be set from Request.Form. I think you might want to change your code as follows:
// change following
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
// to this?
employeeToAdd.Position = new Position {Title = (string)Request.Form["Positions"]};
Good luck!