"The INSERT statement conflicted with the FOREIGN KEY constraint - c#

Error message:
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_UserProfile_UserLogin". The conflict occurred in database
"ToDoDB", table "dbo.UserLogin", column 'UserLoginID'. The statement
has been terminated.
What could this mean?
I am trying to build a simple log in and profile MVC5 web app. I created my table in SQL Express.
Firstly here is my model for the sign up page:
public class UserSignUp
{
[Key]
public int UserLoginID { get; set; }
//Foregin key for the login table - First name, last name, creation date, and email
public int UserProfileID { get; set; }
[Required(ErrorMessage = "Username is required")]
[Display(Name = "Username")]
public string Username { get; set; }
[Required(ErrorMessage = "Password is required")]
[Display(Name = "Password")]
public string Password { get; set; }
[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is required")]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreationDate { get; set; }
[Required(ErrorMessage = "Valid email is required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
So the UserLoginID is the primary key from the UserLogin table and the UserProfileID is the primary from the UserProfile table. I set the foreign key of the UserProfile table to UserLoginID from the UserLogin.
Here is my model for creating a new user:
public class UserProfileManager
{
public void AddUserAccount(UserSignUp newUser)
{
// create database connection
using (ToDoDBEntities db = new ToDoDBEntities())
{
// Collect viewmodel data
// Here building goes by object type and not foregin key relationship
UserLogin UL = new UserLogin();
UL.Username = newUser.Username;
UL.Password = newUser.Password;
// Add the UserLogin object I just built to the database
db.UserLogins.Add(UL);
db.SaveChanges();
UserProfile UP = new UserProfile();
// establish connection to UL by establishing foreign key relationship
UP.UserLoginID = newUser.UserLoginID;
UP.FirstName = newUser.FirstName;
UP.LastName = newUser.LastName;
UP.CreationDate = newUser.CreationDate;
UP.Email = newUser.Email;
// Add UserProfile object to databse and save changes
db.UserProfiles.Add(UP);
db.SaveChanges();
}
}
//Check if user is real before login is allowed
public bool isLoginReal(string LoginName)
{
using (ToDoDBEntities DB = new ToDoDBEntities())
{
// Return the user from the DB whose login name matches the LoginName string passed in as perameter
return DB.UserLogins.Where(o => o.Username.Equals(LoginName)).Any();
}
}
}
My AddUserAccount is where I think I am having issues. So I start by building the UserLogin object and adding and saving to the database. That seems to work out actually. But the next step where I build, add, and save the UserProfile object doesn't seem to work. At least the database doesn't get updated.
Here is the controller handling the actions:
public class AccountController : Controller
{
// GET: Account
public ActionResult Index()
{
return View();
}
#region signup methods
// Get method for signup page
public ActionResult SignUpPage()
{
return View();
}
// Post method for signup page - post to db
[HttpPost]
// Pass in the UserSign up model object to be built
public ActionResult SignUpPage(UserSignUp USUV)
{
// Form is filled out and then method is entered
if (ModelState.IsValid)
{
// Form is filled out and database connection is established if form is valid
UserProfileManager UPM = new UserProfileManager();
if (!UPM.isLoginReal(USUV.Username))
{
// data access . adduseraccount from entity manager (where model objects are built)
UPM.AddUserAccount(USUV);
FormsAuthentication.SetAuthCookie(USUV.FirstName, false);
return RedirectToAction("Welcome", "Home");
}
else
{
}
}
return View();
}
#endregion
}
To my (noob) eye everything looks good. The SignUpPage is received, then a new UserSignUp object is passed into the Post action, and entity framework object (UserProfileManager) is built, the form is authenticated and the user gets either redirected to the Welcome view or the user gets returned to the signup view.
Any chance someone can help me figure out what I am either missing or doing wrong? I included a picture of the database design for reference (I know even less about database then MVC).

Ah yes, after the problem is here:
UP.UserLoginID = newUser.UserLoginID;
It's not newUser, it should be:
UP.UserLoginID = UL.UserLoginID;
Because you just added the object UL to the database, to get the generated ID of the inserted object, you have to call it, not the newUser object.

Related

ArgumentException: The key value at position 0 of the call to 'DbSet<News>.Find' was of type 'string', which does not match the property type of int'

This error is thrown when I try to edit my post.
Here is my code:
public async Task<IActionResult> Edit(string id)
{
if (id == null)
{
return NotFound();
}
// issue
var news = await _context.News.FindAsync(id);
if (news == null)
{
return NotFound();
}
return View(news);
}
The debugger stops the code at
var news = await _context.News.FindAsync(id);
Code for my model is
public int id { get; set; }
[Required(ErrorMessage = "Enter your name.")]
public string Author { get; set; }
[Required(ErrorMessage = "Enter the title.")]
public string Title { get; set; }
[Required(ErrorMessage = "Enter the issued date.")]
[DataType(DataType.Date)]
public DateTime IssueDate { get; set; }
[Required(ErrorMessage = "Enter a message.")]
[DataType(DataType.MultilineText)]
public string Body { get; set; }
Any idea on how to fix this?
According to the documentation:
FindAsync(Object[])
Finds an entity with the given primary key values. If an entity with
the given primary key values is being tracked by the context, then it
is returned immediately without making a request to the database.
Otherwise, a query is made to the database for an entity with the
given primary key values and this entity, if found, is attached to the
context and returned. If no entity is found, then null is returned.
Therefore, if the primary key in your case has int type, than the FindAsync() parameter should be the same type int.
The most reliable way would be
var _id=Convert.ToInt32(id);
var news = await _context.News.FirstOrDefaultAsync(i=>i.id==_id);
but maybe it is better to change an action?
public async Task<IActionResult> Edit(int? id)

C# Passing params to another view

I have a set up where a Company can have none or one or many clients. So there is no strict relationship between the Client table and the Company table. I have created a Search view where all companies are populated. Then using a button a client can be attached to the company. I thought using an ActionLink I would be able to achieve this, so my Search (view) has,
#Html.ActionLink("Book", "Book", new { id = a.CompanyId })
Where the Model is looped over to get all the company list. Now when I click the link, it populates the Address with the right params, Companies/Book/1 the ID I am playing with is 1. Which is correct, however the View I am landing at is a new Customer Model.
public class CustomerModel
{
[HiddenInput(DisplayValue = true)]
public long CompanyId { get; set; }
[HiddenInput(DisplayValue = false)]
public long CustomerId { get; set; }
[Display(Name = "Customer Name")]
[Required(ErrorMessage = "* required")]
public string CustomerName { get; set; }
[Display(Name = "Address Line 1")]
[Required(ErrorMessage = "* required")]
public string AddressLine1 { get; set; }
[Display(Name = "Postcode")]
[Required(ErrorMessage = "* required")]
public string Postcode { get; set; }
[Display(Name = "Phone Number")]
[Required(ErrorMessage = "* required")]
[RegularExpression(#"\d*", ErrorMessage = "Not a valid phone number")]
public string PhoneNo { get; set; }
}
Even though I am able to see the ID being passed (using FireBug) is 1, somehow when I click the button to submit the view to the controller I get a 0. Why would this be? Could anyone help me?
EDIT - 1
This is the controller.
public ActionResult Book()
{
return View(new CustomerModel());
}
[HttpPost]
public ActionResult SaveCustomer(CustomerModel model)
{
_companyService.SaveCustomer(model);
return RedirectToAction("Index");
}
I have tried using the CompanyId instead of id, it came up with another error.
Before submitting the Form, Address bar has : http://localhost:53294/Companies/Book?CompanyId=1
After submitting the Form, Address Bar has : http://localhost:53294/Companies/SaveCustomer
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Customer_dbo.Company_CompanyId". The conflict occurred in database "BoilerServicing", table "dbo.Company", column 'CompanyId'.
The statement has been terminated.
The save method by itself,
public void SaveCustomer(CustomerModel customer)
{
using (var db = new BoilerServicingDbContext())
{
Customer entity;
if (customer.CustomerId > 0)
{
entity = db.Customers.First(x => x.Id == customer.CustomerId);
}
else
{
entity = new Customer();
db.Customers.Add(entity);
}
entity.Name = customer.CustomerName;
entity.TelephoneNumber = customer.PhoneNo;
entity.AddressLine1 = customer.AddressLine1;
entity.PostCode = customer.Postcode;
entity.CompanyId = customer.CompanyId;
db.SaveChanges();
}
}
Okay, after trying so many ways, I have come to this. I changed the Action method on the controller.
public ActionResult Book(long id)
{
return View(new CustomerModel
{
CompanyId = id
});
}
This seems to have passed in the CompanyId I am passing into the Book view. Took me a while, but I got there. Thanks for all your help !

#Html.Dropdownlist list isn't saving to database

In the below example I'm trying to save the Id from the Asp.Net Identity, "aspnetusers" table "Id" column into my "Application" table. The dropdownlist of "fullname" populates, but is not saving to my "application" table when I submit an application. I need the "Id" from the aspnetusers table to be saved to my "application" table after submitting the application. Help would be greatly appreciated!
Controller:
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Index()
{
ViewBag.FullNameList = new SelectList(db.Users,"Id","FullName");
return View();
}
// POST: Applications/Create
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Id,FullName, FirstName,MiddleInitial,LastName,")] Application application)
{
if (ModelState.IsValid)
{
ViewBag.FullNameList = new SelectList(db.Users, "Id", "FullName", application.ApplicationUser);
db.Applications.Add(application);
db.SaveChanges();
return RedirectToAction("Thanks");
}
}
View:
<p>
#Html.DropDownListFor(m => m.FullName, (SelectList)ViewBag.FullNameList, "Select Loan Officer")
</p>
Model:
public class Application
{
public int ApplicationId { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[DisplayName("Last Name")]
public string LastName { get; set; }
public virtual string FullName {get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Many things wrong:
Your post action accepts Application, which doesn't have a FullName property.
Even if it did, your Bind attribute doesn't included it.
You can't have a ViewBag member holding your select list with the same name as the field you're trying to post. Change it to something like ViewBag.FullNameChoices.
The posted value would be the Id of the "loan officer" user and you're doing nothing with it. If you actually had a foreign key property, you could post directly to that, but instead you're just relying on EF to create an implicit property which you have no access to. In your post action, you would need to look up the user with that Id from the database and then set your ApplicationUser property to that.
While not technically wrong, having a property that represents the "loan officer" for an application call ApplicationUser is not intuitive. You should change it to something like LoanOfficer. Also, it looks like your assuming that all users for all time will also be "loan officers", you should probably plan some flexibility by creating a subclass of ApplicationUser for a loan officer or use roles to assign that status.
Are you forgetting to add an [HttpPost]?
Also, your DropDownList might be:
#Html.DropDownList("FullName", ViewBag.FullName, "Select Loan Officer")

Assign ASP.NET Identity Id to foreign table

Its so simply, sadly this new Identity system has to throw a wrench at me.
All I want to do is in my admin, create a client project that is assigned to a user. There's plenty of documentation on how to get the user to create their own stuff. But I need to have the admin create only this time.
The page loads but then on post I get this error which makes no sense in this situation based off of what I've read, "There is no ViewData item of type 'IEnumerable' that has the key 'userId'." I'm clearly not using a ViewData and it clearly says "userId" on the dropdown.
The model should validate!
MODEL
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ClientProject> ClientProjects { get; set; }
}
public class ClientProject
{
public int Id { get; set; }
[Display(Name = "Project")]
[Required(ErrorMessage = "A project name is required.")]
public string Name { get; set; }
// ForeignKey => dbo.IdentityUser
[Display(Name = "Client")]
[Required(ErrorMessage = "Please select a client account to associate with.")]
public virtual ApplicationUser User { get; set; }
}
CONTROLLER
// GET: /Admin/ClientProjects/Create
public ActionResult Create()
{
ViewBag.ProjectStatusId = new SelectList(Db.ProjectStatuses, "Id", "Name");
ViewBag.Users = new SelectList(UserManager.Users.ToList(),"Id", "UserName");
return View();
}
// POST: /Admin/ClientProjects/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="Id,Name,ProjectStatusId")] ClientProject clientproject, string userId)
{
var client = UserManager.FindById(userId);
if (ModelState.IsValid)
{
clientproject.User = client;
Db.ClientProjects.Add(clientproject);
await Db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.ProjectStatusId = new SelectList(Db.ProjectStatuses, "Id", "Name", clientproject.ProjectStatusId);
return View(clientproject);
}
VIEW
#Html.DropDownList("userId", (IEnumerable<SelectListItem>)ViewBag.Users, "--Select Client Account--", new { #class = "form-control" })
ViewData is MVC's internal representation of your view model, a key-value dictionary to which it refers when trying to bind the data you pass to the model you assign to the view. So in your case, it has no idea what userId is more than likely because your Users view model has an Id property and that's what it expects.
Basically, this MVC error means "I was looking for property [blank] in your model but couldn't find it, therefore I can't complete the task."

Add object to database with username

I am getting a simple object from a form in a MVC4 application
portfolio = {code: xxxxx, quantity: -10}
I need to add the Username to the database when I do this insert which I already know I can get from the HTTPContext.
What would be the best way to include this in the below code. I know I will need to change the object I am adding but what is the right way to do this?
public ActionResult Add(Portfolio portfolio)
{
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
Extend the Portfolio class to include the necessary property:
class Portfolio
{
public string Code { get; set; }
public int Quantity { get; set; }
public string Username { get; set; }
}
When the request comes from client, fields Code and Quantity are initialized, and Username is set to its default value, which is null. So simply initialize it before submitting to DB:
public ActionResult Add(Portfolio portfolio)
{
portfolio.Username = Session["Username"]; // or however the username is stored
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
You could use the User object, I take it all users are first authenticated?
User.Identity.Name
Your Portfolio object could then either have a string for the username or you could use a FK to the 'users' table, and get this ID by passing in the Name property from above.
You could then have either:
class Portfolio
{
...
public string Username { get; set; }
}
If you take this approach, then you would just pass the Portfolio object with the new Username property set.
or
class Portfolio
{
...
public int UserId { get; set; }
}
if you take this approach then you would need to request the UserId from EF context and then populate the Portfolio object with the returned UserId

Categories