如何将SimpleMembership UserProfile链接到具有外键的扩展配置文件?

时间:2013-02-27 23:11:25

标签: c# asp.net-mvc-4 ef-code-first entity-framework-5 simplemembership

我已经没有关于如何使这段代码以我想要的方式工作的想法。我有3个型号,如下所示。

UserProfile and Player 一对一关系

首先创建UserProfile,在注册时将电子邮件确认发送到用户提供的电子邮件中。一旦他们确认了他们的帐户,他们就会被重定向到他们的第一个登录页面,如果经过身份验证,则会重定向到创建新的播放器页面,以提供有关其扩展个人资料的更多信息。在创建页面上,我希望登录用户的UserId与即将创建的播放器相同。当我按原样尝试代码时,没有响应,也没有添加或保存到数据库。

Team and Player 一对多关系

非常自我解释,玩家必须属于一个团队。这样就可以重定向到编辑操作,而不是Player.TeamId != null

用户配置

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    public string UserName { get; set; }

    public string Email { get; set; }

    public virtual Player Player { get; set; }
}

团队

    public class Team
    {
    [Key]
    [HiddenInput(DisplayValue = false)]
    public int TeamId { get; set; }

    [Display(Name = "Full Name:")]
    public string Name { get; set; }         

    public virtual ICollection<Player> Players { get; set; }        
}

播放器

public class Player
{
    [Key]
    [ForeignKey("UserProfile")]
    [HiddenInput(DisplayValue = false)]
    public int UserId { get; set; }

    [Required]
    [Display(Name = "Full name")]
    public string FullName{ get; set; }        

    [Display(Name = "Team")]
    public int TeamId { get; set; }

    public virtual Team Team { get; set; }

    public virtual UserProfile UserProfile { get; set; }
 }

SavePlayer

public void SavePlayer(Player player)
    {
        using (var context = new EFootballDb())
        {
            if (player.UserId == 0)
            {
                context.Players.Add(player);
            }
            else if (player.UserId > 0)
            {
                 var currentPlayer = context.Players                        
                    .Single(t => t.UserId == player.UserId);

                context.Entry(currentPlayer).CurrentValues.SetValues(player);                    
            }
            context.SaveChanges();
        }
    }

制作行动

[HttpGet]
    public ActionResult Create()
    {
        PopulateTeamsDropDownList();
        return View();
    }

    [HttpPost]
    public ActionResult Create(Player model)
    {
       try
       {
           if (ModelState.IsValid)
           {
                _dataSource.SavePlayer(model);
                return RedirectToAction("Detail", "Team", new { id = model.TeamId });
            }
        }
        catch (Exception)
        {
            ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
        }
        PopulateTeamsDropDownList(model.TeamId);
        return View(model);
    }


private void PopulateTeamsDropDownList(object selectedTeams = null)
    {
        var teamsQuery = from d in _dataSource.Teams
                         orderby d.Name
                         select d;
        ViewBag.TeamID = new SelectList(teamsQuery, "TeamId", "Name", selectedTeams);
    }

Team and Player之后,所有内容都运行得很好但是我无法将UserProfile and Player链接到Creation。有没有人有想法或不同的方法,我应该尝试?提前谢谢!

2 个答案:

答案 0 :(得分:0)

更新:我正在添加另一种方法来做到这一点。这是一种非常复杂的方式,它可以工作,但最终你不会想要这个。

这是一个痛苦。我也花了很多时间搞清楚这一点。我找到的最近的在线资源是here

请注意该页面上有两个错误。 1)使用Add-Migration而不是Create-Migration 2)更新UsersContext时,请确保包含DbSet的泛型类型。 I.E.

public DbSet<Membership> Membership { get; set; }

按照该链接上的步骤操作。然后你需要多做一点。

对于我的解决方案,我将所有模型移动到模型类库,将UsersContext移动到DAL类库,但这不是必需的。

完成迁移后,您可以添加一个基本类来链接到UserProfile,如此...

public class User
{
    public int UserId { get; set; }

    public string FirstName { get; set; }

    public int UserProfileId { get; set; }

    [ForeignKey("UserProfileId")]
    public UserProfile UserProfile { get; set; }
}

不要忘记更新UsersContext。注意:您可以将UsersContext重命名为您喜欢的任何内容。

public DbSet<User> Users { get; set; }

最后一步是添加一个在AccountController中调用的类来代替WebSecurity.CreateUserAndAccount(...)

public class CustomSimpleMembershipProvider
{
    private readonly UsersContext _db = new UsersContext();

    public bool CreateUser(string username, string password, string firstName)
    {
        var userCreated = false;

        WebSecurity.CreateUserAndAccount(username, password);

        var userProfile = _db.UserProfiles.FirstOrDefault(u => u.UserName == username);

        // Check if user already exists
        if (userProfile != null)
        {
            var member = new Model.User { FirstName = firstName, UserProfile = userProfile };

            try
            {
                _db.Users.Add(member);
                _db.SaveChanges();

                userCreated = true;
            }
            catch (Exception)
            {
                //Add logging or throw whatever exceptions needed
                throw;
            }

        }
        return userCreated;
    }
}

迁移的原因是它允许您在自己的上下文中控制成员资格表。我试图在不同的环境中创建它们但没有成功。我希望这能满足你的需求。

答案 1 :(得分:0)

更简单的方法是创建一个带有UserProfile虚拟外键的类。虚拟用于延迟加载。我这样做的原因是,一旦我进入OAuth,它就会更加容易。

   public class User
    {
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public int UserProfileId { get; set; }

        [ForeignKey("UserProfileId")]
        public virtual UserProfile UserProfile { get; set; }
    }

然后将您的UsersContext从AccountModels.cs移出到您的自定义上下文中。

public class OAuthNoMigrateContext : DbContext
{
    public OAuthNoMigrateContext()
        : base("DefaultConnection")
    {
    }

    public DbSet<User> Users { get; set; }
    public DbSet<UserProfile> UserProfiles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

    }
}

在我的其他答案中创建一个类似的CustomSimpleMembershipProvider。

public class CustomSimpleMembershipProvider
{
    private readonly OAuthNoMigrateContext _db = new OAuthNoMigrateContext();

    public bool CreateUser(string username, string password, User user)
    {
        var userCreated = false;

        WebSecurity.CreateUserAndAccount(username, password);

        var userProfile = _db.UserProfiles.FirstOrDefault(u => u.UserName == username);

        // Check if user exists
        if (userProfile != null)
        {
            user.UserProfile = userProfile;

            try
            {
                _db.Users.Add(user);
                _db.SaveChanges();

                userCreated = true;
            }
            catch (Exception)
            {
                //Add logging or throw whatever exceptions needed
                throw;
            }

        }
        return userCreated;
    }
}

将其添加到Application_Start

Database.SetInitializer(new OAuthNoMigrateInitializer());

注意:我喜欢创建一个检查用户的方法,并从Application_Start中调用。初次化是在您第一次调用DB时启动的,这样做。

private static void ViewDb()
    {
        using (var context = new OAuthNoMigrateContext())
        {
            var user = context.Users.Where(u => u.FirstName.StartsWith("T"));
        }
    }

调用此初始化程序

public class OAuthNoMigrateInitializer : DropCreateDatabaseAlways<OAuthNoMigrateContext>
{
    private readonly CustomSimpleMembershipProvider _provider = new CustomSimpleMembershipProvider();

    protected override sealed void Seed(OAuthNoMigrateContext context)
    {
        //Initialize UserProfile
        WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

        AddUsers(context);
    }

    private void AddUsers(OAuthNoMigrateContext context)
    {
        if (WebSecurity.UserExists("username")) return;

        var user = new User
        {
            FirstName = "FName",
            LastName = "LName"
        };

        _provider.CreateUser("username", "password", user);
    }
}