-
Dec 9th, 2015, 02:19 PM
#1
Thread Starter
PowerPoster
[RESOLVED] MVC, EF6, CodeFirst, IdentityUser, foreign table
Okay, MVC is becoming a major headache to learn and it shouldn't be so difficult! I am using the User Accounts in MVC. I have been able to create user profile information in a foreign table, but I am having a major hard time extracting information from this foreign table to compare at account registration, to ensure it is not already in the database. If it exists in the database (MSSQL), then to show a model error. If the value does not exist, register the account.
First thing I did was to create a class file called TenantInfo.cs under the models folder with the following data:
Code:
using Microsoft.AspNet.Identity.EntityFramework;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
public class Tenant : IdentityUser
{
public virtual TenantInfo TenantInfo { get; set; }
}
[Table("TenantInfo")]
public class TenantInfo
{
[Column("TenantInfoID")]
public int TenantInfoID { get; set; }
[Column("CompanyName")]
[DataType(DataType.Text)]
[Display(Name = "Company Name")]
[MinLength(4), MaxLength(250), StringLength(250, ErrorMessage = "The {0} must be from {1} to {2} characters long.", MinimumLength = 4)]
[Index("IX_TenantInfo_CompanyName", IsUnique = true)]
[Required(AllowEmptyStrings = false,
ErrorMessage = "The {0} is required. Please enter in a valid {0}."),
ConcurrencyCheck]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string CompanyName { get; set; }
[Column("FirstName")]
[DataType(DataType.Text)]
[Display(Name = "First Name")]
[MinLength(1), MaxLength(25), StringLength(25, ErrorMessage = "The {0} must be from {1} to {2} characters long.", MinimumLength = 1)]
[Required(AllowEmptyStrings = false,
ErrorMessage = "The {0} is required. Please enter in a valid {0}."),
ConcurrencyCheck]
public string FirstName { get; set; }
[Column("MiddleName")]
[DataType(DataType.Text)]
[Display(Name = "Middle Name")]
[MinLength(0), MaxLength(25), StringLength(25, ErrorMessage = "The {0} must be from {1} to {2} characters long.", MinimumLength = 0)]
[ConcurrencyCheck]
[DisplayFormat(ConvertEmptyStringToNull = true)]
public string MiddleName { get; set; }
[Column("LastName")]
[DataType(DataType.Text)]
[Display(Name = "Last Name")]
[MinLength(1), MaxLength(25), StringLength(25, ErrorMessage = "The {0} must be from {1} to {2} characters long.", MinimumLength = 1)]
[Required(AllowEmptyStrings = false,
ErrorMessage = "The {0} is required. Please enter in a valid {0}."),
ConcurrencyCheck]
public string LastName { get; set; }
public class TenantDbContext : IdentityDbContext<Tenant>
{
public TenantDbContext() : base("DefaultConnection") { }
public DbSet<TenantInfo> TenantInfo { get; set; }
}
}
I then went into AccountController.cs and updated the Register action with the following:
Code:
var user = new ApplicationUser
{
TenantInfo = new TenantInfo
{
CompanyName = model.CompanyName,
FirstName = model.FirstName,
MiddleName = model.MiddleName,
LastName = model.LastName,
},
UserName = model.Email,
Email = model.Email
};
The respective register CSHTML and RegisterViewModel was updated with the additional fields.
Within the IdentityModels.cs file, under ApplicationUser, I added the following:
Code:
[Column("TenantInfoID")]
public int TenantInfoID { get; set; }
[ForeignKey("TenantInfoID")]
public virtual TenantInfo TenantInfo { get; set; }
I then updated the ApplicationDbContext within IdentityModels.cs to the following:
Code:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired<TenantInfo>(ti => ti.TenantInfo);
modelBuilder.Entity<ApplicationUser>()
.ToTable("Users")
.Property(p => p.Id)
.HasColumnName("UserID");
modelBuilder.Entity<ApplicationUser>()
.Property(p => p.Email)
.HasColumnName("EmailAddress");
modelBuilder.Entity<ApplicationUser>()
.Property(p => p.UserName)
.HasColumnName("Username");
modelBuilder.Entity<IdentityRole>()
.ToTable("Roles")
.Property(p => p.Id)
.HasColumnName("RoleID");
modelBuilder.Entity<IdentityUserRole>()
.ToTable("UserRoles")
.Property(p => p.UserId)
.HasColumnName("UserID");
modelBuilder.Entity<IdentityUserRole>()
.Property(p => p.RoleId)
.HasColumnName("RoleID");
modelBuilder.Entity<IdentityUserClaim>()
.ToTable("UserClaims")
.Property(p => p.UserId)
.HasColumnName("UserID");
modelBuilder.Entity<IdentityUserClaim>()
.Property(p => p.Id)
.HasColumnName("UserClaimsID");
modelBuilder.Entity<IdentityUserLogin>()
.ToTable("UserLogins")
.Property(p => p.UserId)
.HasColumnName("UserID");
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions
.Remove<OneToManyCascadeDeleteConvention>();
}
public DbSet<TenantInfo> TenantInfo { get; set; }
}
My issue right now, is that I am trying to prevent duplicate Company Names from being registered. I have no idea how this is checked. Been searching for days online and getting no where fast.
Right now, if I register a second user with the same company name, I get the error about the unique constraint, which is expected. I also tried catching the SQLException and checking to see if the error contains the first part of the exception and it still shows the error when trying to register. It should show a model error if the company name already exists, but it doesn't. Some people have said to use the [Remote] DataAnnotation, but this does not make sense to me at all.
Any suggestions would be appreciated.
-
Dec 11th, 2015, 01:14 PM
#2
Fanatic Member
Re: MVC, EF6, CodeFirst, IdentityUser, foreign table
How about in your controller, before calling the CreateUser method or (whatever it is called) query to see if the company name exists.
On the bus at the minute. Will have another look when I that home.
-
Dec 11th, 2015, 03:13 PM
#3
Fanatic Member
Re: MVC, EF6, CodeFirst, IdentityUser, foreign table
Here we go BrailleSchool.
First of all your post was nicely detailed so I was able to replicate your code easily in a new mvc application.
Secondly this code will check for a company and add an error to the ModelState if it finds it already exists.
The existing code is wrapped in the else statement.
Code:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
// Add condition to check if company exists
var context = new ApplicationDbContext();
var companyExists = context.Users.Where(u => u.TenantInfo.CompanyName == model.CompanyName).Count() > 0;
context.Dispose();
if (companyExists)
{
ModelState.AddModelError("", "Company already exists");
}
else
{
// Wrap existing code in else statement
var user = new ApplicationUser
{
TenantInfo = new TenantInfo
{
CompanyName = model.CompanyName,
FirstName = model.FirstName,
MiddleName = model.MiddleName,
LastName = model.LastName,
},
UserName = model.Email,
Email = model.Email
};
//var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
There is one part element of this solution I personally do not like and that is the ApplicationDBContext instance
Code:
var context = new ApplicationDbContext();
A lot of the methods on UserManager are extension methods. you may prefer to write an extension method for FindByCompany(), especially if you need the search to be async, you could write an async extension method
-
Dec 11th, 2015, 07:59 PM
#4
Thread Starter
PowerPoster
Re: MVC, EF6, CodeFirst, IdentityUser, foreign table
Thanks for the information. I am sure you had to use the LINQ to do this. But, I am very new to MVC and I find it a pain in the BEEP!
At least I'll learn something new here and I'll take a look at the code.
Originally Posted by davidrobin
Here we go BrailleSchool.
First of all your post was nicely detailed so I was able to replicate your code easily in a new mvc application.
Secondly this code will check for a company and add an error to the ModelState if it finds it already exists.
The existing code is wrapped in the else statement.
Code:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
// Add condition to check if company exists
var context = new ApplicationDbContext();
var companyExists = context.Users.Where(u => u.TenantInfo.CompanyName == model.CompanyName).Count() > 0;
context.Dispose();
if (companyExists)
{
ModelState.AddModelError("", "Company already exists");
}
else
{
// Wrap existing code in else statement
var user = new ApplicationUser
{
TenantInfo = new TenantInfo
{
CompanyName = model.CompanyName,
FirstName = model.FirstName,
MiddleName = model.MiddleName,
LastName = model.LastName,
},
UserName = model.Email,
Email = model.Email
};
//var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
There is one part element of this solution I personally do not like and that is the ApplicationDBContext instance
Code:
var context = new ApplicationDbContext();
A lot of the methods on UserManager are extension methods. you may prefer to write an extension method for FindByCompany(), especially if you need the search to be async, you could write an async extension method
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|