Results 1 to 4 of 4

Thread: [RESOLVED] MVC, EF6, CodeFirst, IdentityUser, foreign table

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2005
    Location
    Debug.Print
    Posts
    3,885

    Resolved [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.

  2. #2
    Fanatic Member
    Join Date
    Oct 1999
    Location
    England
    Posts
    982

    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.


    Things I do when I am bored: DotNetable

  3. #3
    Fanatic Member
    Join Date
    Oct 1999
    Location
    England
    Posts
    982

    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


    Things I do when I am bored: DotNetable

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Apr 2005
    Location
    Debug.Print
    Posts
    3,885

    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.

    Quote Originally Posted by davidrobin View Post
    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
  •  



Click Here to Expand Forum to Full Width