Hey could someone please help me submit this form? Here is my ViewModel that I made. This is my third day trying to pick up MVC so I'm still new to this.
public class EmployeeAllData
{
public Employees Employees { get; set; }
public PermissionModel PermissionModel { get; set; }
}
Here is my controller for the form submit. I'm starting with the permission table and I'm not having any luck. It keeps giving me this error: NullReferenceException was unhandled by user code. I tried hard coding values and it updated the permissions table just fine. I can't find out why I'm not getting a value back from Employees.
[HttpPost]
public ActionResult Create(EmployeeAllData viewModel)
{
var permission = new PermissionModel
{
EmployeesId = Convert.ToByte(viewModel.Employees.Id),
TimeStamp = DateTime.Now,
PermissionVal = viewModel.Employees.Permissions
};
_context.PermissionModels.Add(permission);
_context.SaveChanges();
return RedirectToAction("EmployeeList", "Employee");
}
Any ideas?
UPDATE
I think my problem is with my ViewModel. The code below runs fine and creates a new employee in the database.
public ActionResult Create(Employees employees)
{
_context.Employeeses.Add(employees);
_context.SaveChanges();
return RedirectToAction("EmployeeList", "Employee");
}
I did not need to use a viewModel. Here is my final code that now works.
[HttpPost]
public ActionResult Create(Employees employees)
{
_context.Employeeses.Add(employees);
_context.SaveChanges();
var permission = new PermissionModel()
{
EmployeesId = Convert.ToByte(employees.Id),
TimeStamp = DateTime.Now,
PermissionVal = employees.Permissions
};
_context.PermissionModels.Add(permission);
_context.SaveChanges();
return RedirectToAction("EmployeeList", "Employee");
}
Related
I'm using VS2013 and building a simple MVC5 app with EF6 (learning C#/MVC)
The app is simple. There's a table and an SP in an SQL database that serves up sequential job nos to users via web page. This SP will also be called by a another app so the biz logic is in the SP.
The SP takes a username and returns a JobNo (PK), which is derived as Max(JobNo) + 1
I'm using DB First (as I don't currently understand enough about migrations to production with code first and nearly everything I will write has to work with existing DB's and existing SP's)
I created the model from the DB using ADO.NET and chose the table and a few SP's. This created the following:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace JobNoServer.Models
{
using System;
using System.Collections.Generic;
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
}
}
The problem I've got is that when I call the SP (user clicks "Get new Job No"), I only want to pass the username. The CreateDateTime will be current datetime (set in SP) and JobNo is determined in SP.
I tried removing the setter in class members:
public int JobNo1 { get; }
but then I get the error "must declare a body because it is not marked abstract or extern", but can't figure out how to fix this.
The other issue I have is that the controller created this Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "JobNo1,CreateDateTime,UserName")] JobNo jobNo)
{
if (ModelState.IsValid)
{
db.JobNo.Add(jobNo);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(jobNo);
}
But I can't pass a job no, as it doesn't exist until after Create is called. When I remove the JobNo from the add method, I get a message saying there is no overload that takes zero params. When i look at the definition of Add it's some kind of generic class and the create view is saying job no is mandatory
public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable, IInternalSetAdapter where TEntity : class
{
Could someone point me in the right direction of how to have a create method that takes just the UserName, calls the SP and gets the return value?
There are lot's of way to do this let's look at a few options:
//add a post method to the JobNo class (makes controller logic dead simple)...
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
public Task Post(){
using (var db = new My Entities){
//any issues found here are purely Data access related!
db.JobNo.Add(UserName);
await db.SaveChangesAsync();
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(JobNo vm)
{
if (ModelState.IsValid)
{
await vm.Post();
RedirectToAction("Index");
}
return View(vm);
}
But first you have to solve the problem of the SP wanting more than just 1 parameter right? Did you map the SP using DB first? Is JobNo class from the SP? There seems to be an "impedance mismatch"
By the looks of it you have your db mapped successfully, however if your db relies on Stored procedures to create PK instead of it's default functionality MVC can get a little confused, which is where you're having troubles.
Have you tried mapping the stored procedure to your context. Basically right click in your edmx diagram and 'update from database'. Instead of adding your tables(which you already did), you'll find the SP under the Stored Procedures and Functions section and add it. Done. Here's the MS how to...
If you map your stored procedure you can simply do something like the following
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.yourSP(youParams probably uName, createDT);
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}
Then you can reference the Create function in your question and just pass it the JobNo object you just created.
If all else fails and you can't seem to map your SP, remember you could always default to exectuting SQL directly against your context, and call your SP that way.
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.Database.SqlQuery<int>("YourProcName #param1, #param2",
new SqlParameter("param1", createDT.toString()),
new SqlParameter("param2", uName));
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}
I'm developing an ASP.NET MVC 4 Application, wherein I need every user to be redirected to his custom page upon login. The users are obtained from the UserProfile class which I have refactored into a separate class file. How do I modify the Redirect To method in the Login (post) Action in a ASP.NET MVC 4 Internet Project to get this functionality? Further how do I pass this data to a User controller that can display information related to this specific user.
I'm using simple Membership as it comes out of the box in an internet application template in ASP.NET MVC 4.
I'm guessing you're talking about this piece of code in the MVC4 template? I'm doing something very similar - upon login, I redirect the user to a page called Index.cshtml listed under the Account controller :
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
{
return RedirectToAction("Index", "Account");
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, LocalizedText.Account_Invalid_User_Or_Password);
return View(model);
}
For user specific data, why not just extend the UsersContext.cs class in the Classes folder, then use WebSecurity.CurrentUserId to retrieve the information that pertains to that user?
Extended UsersContext class :
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public bool IsPromotional { get; set; }
public bool IsAllowShare { get; set; }
}
This is the Index() action on the Account controller that they get redirected to upon login. Here I just call the users context, new up an AccountModel that's bound to the Index.cshtml page, set those attributes in the model, then return the View with the model we've built :
public ActionResult Index()
{
//New up the account model
var account = new AccountModel();
try
{
//Get the users context
var CurrentUserId = WebSecurity.CurrentUserId;
var context = new UsersContext();
var thisUser = context.UserProfiles.First(p => p.UserId == CurrentUserId);
//Set the name
account.Name = thisUser.Name;
//Set the user specific settings
account.IsAllowShare = thisUser.IsAllowShare;
account.IsPromotional = thisUser.IsPromotional;
}
catch (Exception exception)
{
_logger.Error(exception, "Error building Account Model");
}
return View(account);
}
It may not be exactly what you're looking for, but that should get you moving in the right direction.
I'm attempting to create a single Controller class to handle all foreseeable surveys that I'll end up creating in the future. Currently I have a 'Surveys' table with fields: Id, SurveyName, Active. On the 'master' Surveys' Index page I list out every SurveyName found in that table. Each SurveyName is clickable, and when clicked on, the page sends the SurveyName as a string to the receiving controller action. Said controller action looks like this:
//
//GET: /Surveys/TakeSurvey/
public ActionResult TakeSurvey(string surveyName)
{
Assembly thisAssembly = Assembly.GetExecutingAssembly();
Type typeToCreate = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();
object newSurvey = Activator.CreateInstance(typeToCreate);
ViewBag.surveyName = surveyName;
return View(surveyName, newSurvey);
}
Using reflection I am able to create a new instance of the type (Model) designated by the passed-in string 'surveyName' and am able to pass that Model off to a view with the same name.
EXAMPLE
Someone clicks on "SummerPicnic," the string "SummerPicnic" is passed to the controller. The controller, using reflection, creates a new instance of the SummerPicnic class and passes it to a view with the same name. A person is then able to fill out a form for their summer picnic plans.
This works all fine and dandy. The part that I'm stuck at is trying to save the form passed back by the POST method into the correct corresponding DB table. Since I don't know ahead of time what sort of Model the controller will be getting back, I not only don't know how to tell it what sort of Model to save, but where to save it to, either, since I can't do something ridiculous like:
//
//POST: Surveys/TakeSurvey
[HttpPost]
public ActionResult TakeSurvey(Model survey)
{
if (ModelState.IsValid)
{
_db. + typeof(survey) + .Add(survey);
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View();
}
Is there a way to do this, or should I go about this from a whole different angle? My ultimate goal is to have a single Controller orchestrating every simple-survey, so I don't have to create a separate controller for every single survey I end up making down the road.
An alternative solution I can think of is to have a separate method for every survey, and to have which method to call defined inside of every survey's view. For example, if I had a SummerPicnic survey, the submit button would call an ActionMethod called 'SummerPicnic':
#Ajax.ActionLink("Create", "SummerPicnic", "Surveys", new AjaxOptions { HttpMethod = "POST" })
A survey for PartyAttendance would call an ActionMethod 'PartyAttendance,' etc. I'd rather not have to do that, though...
UPDATE 1
When I call:
_db.Articles.Add(article);
_db.SaveChanges();
This is what _db is:
private IntranetDb _db = new IntranetDb();
Which is...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace Intranet.Models
{
public class IntranetDb : DbContext
{
public DbSet<Article> Articles { get; set; }
public DbSet<ScrollingNews> ScrollingNews { get; set; }
public DbSet<Survey> Surveys { get; set; }
public DbSet<Surveys.test> tests { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
You can try something like this,
UPDATE:
The built-in UpdateModel will work with generic model see this post, so we got little more work.
[HttpPost]
public ActionResult TakeSurvey(FormCollection form, surveyName)
{
var surveyType = Type.GetType(surveyName);
var surveyObj = Activator.CreateInstance(surveyType);
var binder = Binders.GetBinder(surveyType);
var bindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => surveyObj, surveyType),
ModelState = ModelState,
ValueProvider = form
};
binder.BindModel(ControllerContext, bindingContext);
if (ModelState.IsValid)
{
// if "db" derives from ObjectContext then..
db.AddObject(surveyType, surveyObj);
db.SaveChanges();
// if "db" derives from DbContext then..
var objCtx = ((IObjectContextAdapter)db).ObjectContext;
objCtx.AddObject(surveyType, surveyObj);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View();
}
Check this two know the diff between DbContext and ObjectContext
I ended up with a slightly modified version of Mark's code:
[HttpPost]
public ActionResult TakeSurvey(string surveyName, FormCollection form)
{
//var surveyType = Type.GetType(surveyName);
//var surveyObj = Activator.CreateInstance(surveyType);
// Get survey type and create new instance of it
var thisAssembly = Assembly.GetExecutingAssembly();
var surveyType = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();
var newSurvey = Activator.CreateInstance(surveyType);
var binder = Binders.GetBinder(surveyType);
var bindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => newSurvey, surveyType),
ModelState = ModelState,
ValueProvider = form
};
binder.BindModel(ControllerContext, bindingContext);
if (ModelState.IsValid)
{
var objCtx = ((IObjectContextAdapter)_db).ObjectContext;
objCtx.AddObject(surveyName, newSurvey);
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View();
}
I was running into surveyType being 'null' when it was set to Type.GetType(surveyName); so I went ahead and retrieved the Type via Reflection.
The only trouble I'm running into now is here:
if (ModelState.IsValid)
{
var objCtx = ((IObjectContextAdapter)_db).ObjectContext;
objCtx.AddObject(surveyName, newSurvey);
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
When it tries to AddObject I'm getting the exception "The EntitySet name 'IntranetDb.test' could not be found." I just need to figure out to strip off the prefix 'IntranetDb.' and hopefully I'll be in business.
UPDATE
One thing I completely overlooked was passing the Model to the controller from the View...oh bother. I currently have an ActionLink replacing the normal 'Submit' button, as I wasn't sure how else to pass to the controller the string it needs to create the correct instance of Survey model:
<p>
#Ajax.ActionLink("Create", "TakeSurvey", "Surveys", new { surveyName = ViewBag.surveyName }, new AjaxOptions { HttpMethod = "POST" })
#*<input type="submit" value="Create" />*#
</p>
So once I figure out how to turn 'IntranetDb.test' to just 'test' I'll tackle how to make the Survey fields not all 'null' on submission.
UPDATE 2
I changed my submission method from using an Ajax ActionLink to a normal submit button. This fixed null values being set for my Model values after I realized that Mark's bindingContext was doing the binding for me (injecting form values onto the Model values). So now my View submits with a simple:
<input type="submit" value="Submit" />
Back to figuring out how to truncate 'IntranetDb.test' to just 'test'...
Got It
The problem lies in my IntranetDb class:
public class IntranetDb : DbContext
{
public DbSet<Article> Articles { get; set; }
public DbSet<ScrollingNews> ScrollingNews { get; set; }
public DbSet<SurveyMaster> SurveyMaster { get; set; }
public DbSet<Surveys.test> tests { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
objCtx.AddObject(surveyName, newSurveyEntry); was looking for an entry (an "EntitySet") in the IntranetDb class called "test." The problem lies in the fact that I don't have an EntitySet by the name of "test" but rather by the name of "tests" with an 's' for pluralization. Turns out I don't need to truncate anything at all, I just need to point to the right object :P Once I get that straight I should be in business! Thank you Mark and Abhijit for your assistance! ^_^
FINISHED
//
//POST: Surveys/TakeSurvey
[HttpPost]
public ActionResult TakeSurvey(string surveyName, FormCollection form)
{
//var surveyType = Type.GetType(surveyName);
//var surveyObj = Activator.CreateInstance(surveyType);
// Create Survey Type using Reflection
var thisAssembly = Assembly.GetExecutingAssembly();
var surveyType = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();
var newSurveyEntry = Activator.CreateInstance(surveyType);
// Set up binder
var binder = Binders.GetBinder(surveyType);
var bindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => newSurveyEntry, surveyType),
ModelState = ModelState,
ValueProvider = form // Get values from form
};
var objCtx = ((IObjectContextAdapter)_db).ObjectContext;
// Retrieve EntitySet name for Survey type
var container = objCtx.MetadataWorkspace.GetEntityContainer(objCtx.DefaultContainerName, DataSpace.CSpace);
string setName = (from meta in container.BaseEntitySets
where meta.ElementType.Name == surveyName
select meta.Name).First();
binder.BindModel(ControllerContext, bindingContext); // bind form values to survey object
if (ModelState.IsValid)
{
objCtx.AddObject(setName, newSurveyEntry); // Add survey entry to appropriate EntitySet
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View();
}
It's kind of bloated but it works for now. This post helped me get the EntitySet from the Survey object itself so I didn't need to worry about establishing some sort of EntitySet naming convention.
The main problem I see is to bind to the model to the TakeSurvey POST method. If you want different types of survey models should be handled by this method and MVC should bind to this model before calling the action, I believe you can have a wrapper model class over all such generic model, say SurveyModel and use custom model binder to bind to these models.
public class SurveyModel
{
public string GetSurveyModelType();
public SummerPicnicSurvey SummerPicnicSurvey { get; set; }
public PartyAttendanceSurvey PartyAttendanceSurvey { get; set; }
}
Then write a custom mobel binder to bind this model. From the request form fields we can see what type of survey model is posted and then accordingly fetch all the fields and initialize the SurveyModel class. If SummerPicnicSurvey is posted then class SurveyModel will be set with this class and PartyAttendanceSurvey will be null. Example custom model binder.
From the controller action TakeSurvey POST method, You can update db like this:
[HttpPost]
public ActionResult TakeSurvey(SurveyModel survey)
{
if (ModelState.IsValid)
{
if(survey.GetSurveyModelType() == "SummerPicnicSurvey")
_db.UpdateSummerPicnicSurvey(survey.SummerPicnicSurvey);
else if (survey.GetSurveyModelType() == "PartyAttendanceSurvey")
_db.UpdateSummerPicnicSurvey(survey.PartyAttendanceSurvey);
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View();
}
Instead of SurveyModel encapsulating the other surveys you can have inheritance and use .net as to typecast with a check and use the Model.
Having said this, I think there is no harm in using different methods for each model. This will enable you to unit test the code well. Too many if else is not healthy to maintain. Or you can transfer the generic model SurveyModel to the repository or data access layer and let it handle that in a polymorphic way. I would prefer more small functions and keep the code clean.
Edit: The inheritance way:
public class SurveyModel
{
public virtual bool Save();
}
public partial class SummerPicnicSurvey : SurveyModel
{
public bool Save(SummerPicnicSurvey survey)
{
using(var _dbContext = new MyContext())
{
_dbContex.SummerPicnicSurveys.Add(survey);
_dbContex.SaveChanges();
}
}
}
[HttpPost]
public ActionResult TakeSurvey(SurveyModel survey)
{
if (ModelState.IsValid)
{
survey.Save();
return RedirectToAction("Index", "Home");
}
return View();
}
Any new Survey model type you add has to implement the SaveChanges or Save method, Which would call the proper dbcontext method. The controller action would just call Save on the generic `SurveyModel' reference passed to it. Thus the action will be closed for modification but open for modification. The open-close design principle.
I using MVC-Viewmodel in my project, my problem is that even if i CTRL-click two values in my listbox It only grabs one value. I want users to be able to select two values but I dont know why it doesnt happen any tips is appreciated!
Here is my GET n POST action inside my controller:
public ActionResult Create()
{
CreateViewModel model = new CreateViewModel();
List<CoreValue> corevalues = Arep.getallC();
model.CoreValuess = new MultiSelectList(corevalues, "CID", "Cname");
return View(model);
}
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if (ModelState.IsValid)
{
Question q = new Question();
var CoreValueID = int.Parse(model.Cname);
var getallC = Arep.getbycid(CoreValueID);
q.CoreValue.Add(getallC);
q.QuestionText = model.QuestionText;
Arep.addquestion(q);
Arep.save();
return RedirectToAction("Index");
}
return View(model);
This is inside my CreateViewModel:
public MultiSelectList CoreValues { get; set; }
And this is inside my View:
#Html.ListBoxFor(model => model.Cname,Model.Corevaluess)
What seem to be the problem?
Thanks in Advance!
Best Regards
Spelling errors aside, I believe the following is why this is failing:
In your ListBoxFor method, you are using model.Cname. By this, I believe you mean "choose the cName of selected CoreValues". However (and I'm guessing because I can't see your model), the Cname property on the CreateViewModel is of type string. Because of this, you are only ever going to have one value at a time. You need a property that is of type IEnumerable in order to hold multiple selections.
Update your model to the following:
public class CreateViewModel
{
public IEnumerable<string> SelectedValues { get; set; }
public IEnumerable<CoreValue> CoreValues { get; set; }
}
SelectedValues will be used to contain the selected values on the post. We can also add items to it to signify what should be automatically selected when the view is created.
In your controller do the following:
public ActionResult Create()
{
CreateViewModel model = new CreateViewModel();
model.CoreValues = Arep.getallC();
return View(model);
}
Lastly, update the view:
#Html.ListBoxFor(m => m.SelectedValues, new MultiSelectList(Model.CoreValues, "CID", "Cname"))
Now, whenever you post, you should be able to see the values that a user selected.
EDIT: I'm not completely sure what some of your methods do so I'm taking a guess.
The POST method for Create:
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if (ModelState.IsValid)
{
foreach(var CoreValueID in model.SelectedValues)
{
Question q = new Question();
var getallC = Arep.getbycid(CoreValueID);
q.CoreValue.Add(getallC);
q.QuestionText = model.QuestionText;
Arep.addquestion(q);
}
Arep.save();
return RedirectToAction("Index");
}
return View(model);
}
you would need to pass selectedvalues as below
List<CoreValue> selectedvalues = Null;
model.CoreValuess = new MultiSelectList(corevalues, "CID", "Cname",selectedvalues);
return View(model);
Refer MSDN link and this helpful Article
As stated above, the ASP.Net Mvc wants to have a list of string, but the same thing can be achieved by using the classic ASP style
Request.Form["CoreValues"]
this will provide as comma separated values.
I have a viewmodel that contains a product and SelectList of categories.
public class AdFormViewModel
{
public AmericanAds.Model.Ad Ad { get; set; }
public SelectList Categories { get; set; }
public AdFormViewModel(AmericanAds.Model.Ad ad, SelectList categories)
{
Ad = ad;
Categories = categories;
}
}
When adding a new product, if validation fails for category dropdown I get below error message.
The model item passed into the dictionary is of type 'AmericanAds.Model.Ad' but this dictionary requires a model item of type 'AmericanAds.Controllers.AdFormViewModel'.
Here is the controller for create action.
public ActionResult Create()
{
AdFormViewModel data = new AdFormViewModel(
null,
new SelectList(_repository.CategoryList().ToList(), "CategoryId", "CategoryName")
);
return View(data);
}
//
// POST: /Ad/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Ad ad)
{
if (ModelState.IsValid)
{
try
{
_repository.AddAd(ad);
return RedirectToAction("Index");
}
catch
{
return View(ad);
}
}
else
{
return View(ad);
}
}
What am I missing?
As you can tell, I am very new to ASP.Net MVC.
Thanks!
It's because your Create view requires a model of type AdFormViewModel but in your Create action (the one with the [AcceptVerbs(HttpVerbs.Post)] attribute) you return a model of type Ad (see the lines where it says return View(ad)).
Like the exception message says ; It requires an AmericanAds.Controllers.AdFormViewModel but you are sending an AmericanAds.Model.Ad.
And no, I don't think this has anything to do with the validation.