ASP.Net MVC 4 SimpleMembership UserID as Foreign Key - asp.net-mvc-4

Dear SO members please help me to get unstuck in what I thought should be an easy task but I have been stuck in for 2 days. I have a couple of tables that need to have a FK to the UserId of the currently logged in user. Here is an example
UserProfile
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string ManagerName { get; set; }
public string PhoneNumber { get; set; }
}
A customer entity
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string Names { get; set; }
[ForeignKey("UserProfile")]
public int UserId { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
The DB Context
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Customer> Customers { get; set; }
}
Customer Controller
[HttpPost]
[InitializeSimpleMembership]
[ValidateAntiForgeryToken]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
model.UserProfile = db.UserProfiles.FirstOrDefault(u => u.UserId == WebSecurity.CurrentUserId);
db.Customers.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.UserProfiles, "UserId", "UserName", model.UserId);
return View(model);
}
Also tried
[HttpPost]
[InitializeSimpleMembership]
[ValidateAntiForgeryToken]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
model.UserId = WebSecurity.GetUserId(User.Identity.Name);
db.Customers.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.UserProfiles, "UserId", "UserName", model.UserId);
return View(model);
}
In each case I get the error message
"The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Customers_dbo.UserProfile_UserId". The conflict occurred in database "ValueCardProject", table "dbo.UserProfile", column 'UserId'.
The statement has been terminated."
All I want to do is to be able to create tables with a FK to the UserID of the logged in user. For the interest of full disclosure, Customers should not be created directly as users, in my use case the current logged in users is a Retailer, so what I am trying to accomplish is to create a customer record with the FK to RetailerId (which is the UserId in the UserProfile).
Thanks for your time.

I believe your foreign key attribute on UserId is in the wrong place.
you should be doing this instead:
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }

Related

Is it possible to link one table to another with entity framework core without FKs?

I have 2 tables company and user. Company will have one created by user and one modified user - these will be admin users. User will belong to one company but one admin user could create or modify multiple companies.
I'm having a hard time using entity framework core in my .net core app to join company and user so when I get a company record I have the created by and modified user information.
My company and user classes look like this:
public class Company
{
[Key]
public Guid Id { get; set; }
public DateTime Created { get; set; }
public Guid Created_By { get; set; }
public virtual ApplicationUser CreatedByUser { get; set; }
public DateTime Modified { get; set; }
public Guid Modified_By { get; set; }
public virtual ApplicationUser ModifiedByUser { get; set; }
public string Company_Name { get; set; }
}
public class ApplicationUser: IdentityUser<Guid>
{
[Column("ID")]
public override Guid Id { get; set; }
[Column("CREATED")]
public DateTime Created { get; set; }
[Column("CREATED_BY")]
public Guid? CreatedBy { get; set; }
[Column("MODIFIED")]
public DateTime Modified { get; set; }
[Column("MODIFIED_BY")]
public Guid? ModifiedBy { get; set; }
[Column("FIRST_NAME")]
public string FirstName { get; set; }
[Column("LAST_NAME")]
public string LastName { get; set; }
[Column("EMAIL")]
public override string Email { get; set; }
[Column("NORMALIZED_EMAIL")]
public override string NormalizedEmail { get; set; }
[Column("EMAIL_CONFIRMED")]
public override bool EmailConfirmed { get; set; }
[Column("USER_NAME")]
public override string UserName { get; set; }
[Column("NORMALIZED_USER_NAME")]
public override string NormalizedUserName { get; set; }
[Column("COMPANY_ID")]
public Guid CompanyId { get; set; }
[Column("PHONE_NUMBER")]
public override string PhoneNumber { get; set; }
[Column("PHONE_NUMBER_CONFIRMED")]
public override bool PhoneNumberConfirmed { get; set; }
[Column("TITLE")]
public string Title { get; set; }
[Column("ACTIVE")]
public bool Active { get; set; }
[Column("ROLE_ID")]
public int UserRoleId { get; set; }
[Column("TYPE_ID")]
public int TypeId { get; set; }
[Column("PASSWORD_HASH")]
public override string PasswordHash { get; set; }
[Column("SECURITY_STAMP")]
public override string SecurityStamp { get; set; }
[Column("CONCURRENCY_STAMP")]
public override string ConcurrencyStamp { get; set; }
[Column("TWO_FACTOR_ENABLED")]
public override bool TwoFactorEnabled { get; set; }
[Column("LOCKOUT_END")]
public override DateTimeOffset? LockoutEnd { get; set; }
[Column("LOCKOUT_ENABLED")]
public override bool LockoutEnabled { get; set; }
[Column("ACCESS_FAILED_COUNT")]
public override int AccessFailedCount { get; set; }
}
My DbContext class looks like this:
public class DbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
public DbContext(DbContextOptions<DbContext> options) : base(options)
{
}
public virtual DbSet<Company> Companies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>().ToTable("COMPANY").Property<Guid>("Created_By");
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
builder.Entity<ApplicationUser>().ToTable("USER");
builder.Entity<ApplicationUser>().HasKey(x => x.Id);
builder.Entity<ApplicationUserClaim>().ToTable("USER_CLAIMS");
builder.Entity<ApplicationRole>().ToTable("IDENTITY_ROLES");
builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });
builder.Entity<ApplicationUserRole>().ToTable("IDENTITY_USER_ROLES");
}
}
And I was trying to get companies like this:
public async Task<List<Company>> GetAllCompanies()
{
return await _locationDbContext.Companies.ToListAsync();
}
Currently I am getting this error:
System.InvalidOperationException: 'You are configuring a relationship between 'Company' and 'ApplicationUser' but have specified a foreign key on 'Created_By'. The foreign key must be defined on a type that is part of the relationship.'
Is there an easier way to do this? Really all I want is the username of the user that created of modified the company record? If I was doing this with just sql I would just use a basic Join but Im not sure how to do that with entity framework. Worst case I would just get all the companies and then loop through doing a select on the user table where ID = Created_By
As #IvanStoev noticed without FKs it is not possible. But you can still use EF to join 2 tables.
In your case you have to unmap user from company and maybe it is a good idea to make user Guid nullable:
public class Company
{
[Key]
public Guid Id { get; set; }
public string Company_Name { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public Guid Created_By { get; set; }
public Guid Modified_By { get; set; }
[NotMapped]
public ApplicationUser CreatedByUser { get; set; }
// or better
[NotMapped]
public string CreatedByUser { get; set; }
[NotMapped]
public ApplicationUser ModifiedByUser { get; set; }
//or better
[NotMapped]
public string ModifiedByUser { get; set; }
}
and remove
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
you still can join them like this
var companies= (
from c in _locationDbContext.Companies
join uc in _locationDbContext.ApplicatonUser on c.Created_By equals uc.Id
join um in _locationDbContext.ApplicatonUser on c.Modified_By equals um.Id
select new Company
{
....
CreatedByUser = uc,
ModifiedByUser = um
// or usually
CreatedByUser = uc.FirstName + " " + uc.LastName,
ModifiedByUser = um.FirstName + " " + um.LastName,
}).ToList();
This is one way of accomplishing this.
This is a simplified version of class Company:
public class Company
{
public int CompanyID { get; set; }
public string Name { get; set; }
//
// Relations
public string CreatorID { get; set; }
public ApplicationUser Creator { get; set; }
public string LastModifiedByID { get; set; }
public ApplicationUser LastModifiedBy { get; set; }
}
CreatorID and LastModifiedByID will be used by EF for determining the FK's. These are the fields you have to work with when updating the Db, not Creator and LastModfiedBy, yet you can also do it but with more lines of code.
Now, you can add companies the usual way, this code searches for the company, and creates one if not found, just sample code, not serious, really:
var companyName = "My company";
var company = context.Companies.Include(c => c.Creator).Include(c => c.Creator).FirstOrDefault(c => c.Name == companyName);
if (company == null)
{
company = new Company
{
Name = "My company",
CreatorID = user.Id,
LastModifiedByID = user.Id
};
context.Companies.Add(company);
context.SaveChanges();
}
And retrieving the companies with all relations filled is a matter or using calls to Include() and ThenInclude(), like here:
var companies = context.Companies.Include(c => c.Creator).Include(c => c.Creator).ToList();
I omitted all filtering logic for the sake of simplicity.

Error with EF core savecontext using Identity class

I have a quiz sql schema and I am also using ASP.NET Identity. When I attempt to insert an answer from the user into the UserAnswer table I get the error below. It seems like it is trying to insert into the User table but I don't want that?
Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert
duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is
(71ddfebf-18ba-4214-a01e-42ca0f239804). Cannot insert explicit value
for identity column in table 'Questions' when IDENTITY_INSERT is set
to OFF. The statement has been terminated.
foreach (ProfileViewModel pvm in profileViewModels)
{
UserAnswer ua = new UserAnswer();
ua.QuestionId.ID = pvm.Question.ID;
ua.ApplicationUser.Id = userId;
ua.AnswerText = pvm.Answer;
_userAnswerRepository.Create(ua);
}
which just does
protected void Save() => _context.SaveChanges();
and the model is
public class UserAnswer
{
public UserAnswer()
{
this.QuestionId = new Question();
this.ApplicationUser = new ApplicationUser();
}
public int Id { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Question QuestionId { get; set; }
public string AnswerText { get; set; }
}
I guess I need to use virtual and not the actual object for some reason.. The model looked fine but it seems to confused the update
public class UserAnswer
{
public UserAnswer()
{
this.Question = new Question();
this.User = new ApplicationUser();
}
public int Id { get; set; }
public string UserId { get; set; } // FK to ApplicationUser
public int QuestionId { get; set; } // FK to Question
public string AnswerText { get; set; }
public virtual Question Question { get; set; }
public virtual ApplicationUser User { get; set; }
}

Registering a User to .Net Core Project

I am writing a .Net Core project in which I am trying to implement a user registration process. I've used the "basic" template provided by Microsoft as I am writing in Visual Studio 2017.
I have stumpled into problem with how the user is registered. I have created a pretty substantial form with the information I require to complete a registration:
RegisterViewModel:
public class RegisterViewModel
{
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public bool Admin { get; set; }
public bool Manager { get; set; }
public int CustomerID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
public string Mobile { get; set; }
public DateTime? Date_Of_Birth { get; set; }
public string Gender { get; set; }
public string Status { get; set; }
public Customer Customer { get; set; }
}
I have left out some of the unimportant lines from the above, as that is not a part of my problem. The part of displaying my form works as intended but when I try to run my [HttpPost] part I stumble into problems.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//do something else here if user was created.
}
}
}
My database is a relational database (has foreign keys) which means that when I try to add a User through this process and the user doesn't have a CustomerID (added or it isn't set) the display of the "User" index doesn't work (breaks).
In the register code above a user is created in my database however none of the fields that was filled from my form are input to my database. And worst of all the customer ID (which is a foreign key) doesn't get inserted, even though it resides in the model.
How do I pass these variables that I NEED from this register method?
I figured it out.
It seems that Visual Studio was so kind to supply me with an ApplicationUser.cs class that just required modification.
The following line was found in my code posted above:
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
I found the ApplicationUser class and modified it to contain my model:
public class ApplicationUser : IdentityUser
{
public int CustomerID { get; internal set; }
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
public string FullName { get; internal set; }
public string Gender { get; internal set; }
public string CName { get; internal set; }
public DateTime CDate { get; internal set; }
public string MobileNumber { get; internal set; }
public DateTime? DateOfBirth { get; internal set; }
public string Status { get; internal set; }
public DateTime StartDate { get; internal set; }
public DateTime EndDate { get; internal set; }
public string Contact1 { get; internal set; }
public string Contact2 { get; internal set; }
}
Hope this helps someone else out there!

CodeFirst - Update single property

We are using EF5, Code First approach to an MVC4 app that we're building. We are trying to update 1 property on an entity but keep getting errors. Here's what the class looks like which the context created:
public partial class Room
{
public Room()
{
this.Address = new HashSet<Address>();
}
public int RoomID { get; set; }
public Nullable<int> AddressID { get; set; }
public Nullable<int> ProductVersionID { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string RoomName { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Notes { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
Here's our ViewModel for the view:
public class RoomDetailsViewModel
{
//public int RoomID { get; set; }
public string RoomName { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string Notes { get; set; }
public string StateCode { get; set; }
public string CountryName { get; set; }
public string ProductVersion { get; set; }
public int PVersionID { get; set; }
public List<SelectListItem> ProductVersions { get; set; }
public Room Room { get; set; }
}
Here's the Controller Action being called on "Save":
[HttpPost]
public virtual ActionResult UpdateRoom(RoomDetailsViewModel model)
{
var db = new DBContext();
bool b = ModelState.IsValid;
var rooms = db.Rooms;
var rm = rooms.Where(r => r.RoomID == model.Room.RoomID).Single();
//List<Address> address = db.Addresses.Where(a => a.AddressID == rm.AddressID).ToList<Address>();
rm.ProductVersionID = model.PVersionID;
//rm.Address = address;
db.Entry(rm).Property(r => r.ProductVersionID).IsModified = true;
//db.Entry(rm).State = System.Data.EntityState.Modified;
db.SaveChanges();
return View("RoomSaved", model);
}
All this view does is display data and allow the user to change the Product Version (from a SelectList), so, in the Room Entity, all we are updating is the ProductVersionID property, nothing else. We can get the data to display properly but when we click "save", we get this error:
An object of type 'System.Collections.Generic.List`1[[Models.Address,
Web.Mobile.TestSite, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]' cannot be set or removed from the Value
property of an EntityReference of type 'Models.Address'.
As you can see by the Controller Action, we've tried several different things but all seem to produce this error. I've tried to populate the model.Room.Address collection with an Address, without, but still get this error.
I read this StackOverflow article and this article as well but neither have solved my problem.
ANY help with this would be greatly appreciated!
After hours and hours of digging, turns out that EF did not import some of the PK's for my DB tables. What tipped me off to this was on the Room class, the PK RoomID did not have the [Key] attribute on it. I tried to reimport the table through the edmx but it never came through as a key (even though it's clearly marked PK in the DB). So, to get around it, I created a partial class of my DBContext and override the OnModelCreating event and included the key, like so:
public partial class DBContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Models.Room>().HasEntitySetName("Rooms");
modelBuilder.Entity<Models.Room>().HasKey(r => r.RoomID);
}
}
Once this was done, the Action saved the record as hoped.
I hope this helps someone else!

asp.net mvc 3 EF query from one to many relationship

I am new to asp.net mvc and i have problems that i think i would solve them very easy with asp.net web forms. However the project have to be in mvc, so here is the problem.
I got X tables
table1 Users
int user_ID
string username
table2 Friends
int friendshipID
int user_ID
int friend_ID
In table 2, user_ID represents the current user that is logged in. friend_ID represents ids from his friends. Its one to many relationship.
Now what i want to do, is, in user/details/ID view, show all friends of that user.
The query that i want to make is: first select the friend_IDs from table2 where user_ID=id(from querystring), then select every username from table1 where user_ID = friend_ID.
I think this is really easy in SQL, but dont know how to do it with the mvc syntax.
The controller:
//
// GET: /User/Details/5
public ViewResult Details(int id)
{
User user = db.Users.Find(id);
return View(user);
}
The view:
#model Social2.Models.User
<div class="display-label">Friends</div>
<div class="display-field">
#foreach (var friend in #Model.Friends)
{
#friend.User.username;
}
</div>
The view returns wrong results.
Models
public partial class User
{
public User()
{
this.Albums = new HashSet<Album>();
this.Friends = new HashSet<Friend>();
this.Messages = new HashSet<Message>();
this.Posts = new HashSet<Post>();
this.Groups = new HashSet<Group>();
}
public int user_ID { get; set; }
public System.Guid user_UniqueID { get; set; }
public string username { get; set; }
public virtual ICollection<Album> Albums { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual ICollection<Friend> Friends { get; set; }
public virtual ICollection<Message> Messages { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
and from friends table
public partial class Friend
{
public int friendship_ID { get; set; }
public int user_fr_ID { get; set; }
public int friend_ID { get; set; }
public virtual User User { get; set; }
}
also the context
public partial class ASPNETDBEntities : DbContext
{
public ASPNETDBEntities()
: base("name=ASPNETDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Album> Albums { get; set; }
public DbSet<aspnet_Users> aspnet_Users { get; set; }
public DbSet<Friend> Friends { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Picture> Pictures { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<sysdiagram> sysdiagrams { get; set; }
public DbSet<User> Users { get; set; }
}
As the Friends list property is virtual it will not be included with your query. Try using below query to include the Friends.
public ViewResult Details(int id)
{
User user = db.Users.Include("Friends").FirstOrDefault(u => u.user_ID == id);
//Also for each friend get the User:
foreach (var friend in user.Friends.ToList())
{
friend.User = db.Users.Find(friend.friend_ID);
}
return View(user);
}
View:
<table>
#foreach (var friend in #Model.Friends)
{
<tr>
#Html.DisplayFor(modelItem => friend.User.username)
</tr>
}
</table>
Your model classes doesn't appear to be following the convention for the Entity keys. The fields "user_ID" and "friendship_ID" should be UserId and FriendId. Or if you want to key them like that annotate them with [key] attribute.
Make the ViewModel class of your own. Retrieve the data from database and build the model class object. Pass this model class to view i.e. create your view based on this model class.