2

У меня закончились идеи о том, как заставить этот код работать так, как я этого хочу. У меня есть 3 модели, как указано ниже.как связать SimpleMembership UserProfile с расширенным профилем с внешним ключом?

UserProfile and Playerодин-к-одному отношения

UserProfile создан первый, при регистрации emailConfirmation отправляется на адрес электронной почты пользователя, поставляемом. Как только они подтвердят свою учетную запись, они перенаправляются на свою первую страницу входа в систему, если они аутентифицированы, перенаправлены на страницу Создать новую страницу, чтобы предоставить дополнительную информацию об расширенном профиле. На странице создания я хочу, чтобы UserId зарегистрированного пользователя был таким же, как и для создаваемого игрока. Когда я пытаюсь использовать код как есть, ответа нет и ничего не добавляется или сохраняется в базе данных.

Team and Playerодин-ко-многим

Довольно много пояснений, игрок должен принадлежать к команде. Это делает перенаправление на действие «Редактировать» a «нет» как Player.TeamId != null.

UserProfile

[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; } 
} 

Team

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); 
    } 

Все работает просто отлично bewteen Team and Player но я просто не могу получить UserProfile and Player соединить в Творении. У кого-нибудь есть идеи или другой подход, я должен попробовать? Заранее спасибо!

ответ

0

UPDATE: Я добавляю другой способ сделать это. Это очень сложный способ, он работает, но в конце концов вы не захотите этого.

Это боль. Я тоже потратил много времени на это. Самый близкий онлайн-ресурс, который я нашел, был here.

Примечание. На этой странице есть две ошибки. 1) Используйте Add-Migration вместо Create-Migration 2) При обновлении UserContext обязательно включите родовой тип для DbSet. И.Е.

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; 
    } 
} 

Причина для миграции это позволяет контролируйте таблицы членства в своем собственном контексте. Я попытался создать их в отдельных контекстах без успеха. Надеюсь, это даст вам то, что вам нужно.

+0

Это не работает для меня. Я попробовал несколько способов, но пока не повезло. Добавляет и сохраняет UserProfile и ничего больше – Komengem

+0

Komenge. Когда UserProfile сохраняется и вы смотрите на структуру базы данных, выглядит ли она корректно с учетом отношения между вашим UserProfile и связанной таблицей? Кроме того, при попытке сохранить связанный объект вы получаете исключение? – JabberwockyDecompiler

+0

До сих пор я могу сказать, что UserProfile и другие связанные таблицы членства обновляются. У меня нет столбца в UserProfile для хранения данных для Player, так как я получаю UserIfile UserProfile и передаю его в Player как внешний ключ. Я думаю, что 'WebSecurity.CurrentUserId' или любые связанные вызовы приближаются к нулю. Я думаю, что это имеет какое-то отношение к контексту, который я использую, который отличается от того, что использует WebSecurity. – Komengem

0

А гораздо более простой способ сделать это - просто создать класс с виртуальным внешним ключом в UserProfile. Виртуальный для ленивой загрузки. Причина, по которой я это делал, - это когда я добрался до OAuth, это сработало намного легче.

Затем переместите ваши 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. Инициализация запускается при первом обращении к БД, так оно и делается.

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

Это называет это Initializer

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); 
    } 
} 
+0

Не забудьте прокомментировать или удалить [InitializeSimpleMembership] в AccountController.cs, поскольку вы уже инициализируете семя. – JabberwockyDecompiler