Вопрос по razor, asp.net-mvc-3, many-to-many, asp.net-mvc, entity-framework – Сохранение данных отношений «многие ко многим» в MVC Create view
У меня есть некоторые проблемы с отношением «многие ко многим», сохраняющим результаты представления создания.
Я хочу создать страницу для нового профиля пользователя с контрольным списком, который позволяет им выбирать курсы (отношения многие ко многим).
Мой взгляд берет записи изCourses
базы данных и показывает их все с флажками.
Как только пользователь публикует данные, я хочу обновить своиuserprofile
модель, а такжеcourses
отношения многие ко многим. Это код, который я пропустил!
Я новичок в MVC и я занимаюсь исследованиями, но пока не могу.
Я следую этому примеру: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc- приложение
Это модель:
public class UserProfile
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Courses> usercourses { get; set; }
}
public class Courses
{
public int CourseID { get; set; }
public string CourseDescripcion { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
public class UserProfileDBContext : DbContext
{
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Courses> usrCourses{ get; set; }
}
Я также добавил ViewModel:
namespace Mysolution.ViewModels
{
public class AssignedCourseData
{
public int CourseID { get; set; }
public string CourseDescription { get; set; }
public bool Assigned { get; set; }
}
}
Этоcreate
Контроллер, который заполняет флажки курсов:
public ActionResult Create()
{
PopulateCoursesData();
return View();
}
private void PopulateCoursesData()
{
var CoursesData = db.usrCourses;
var viewModel = new List<AssignedCourseData>();
foreach (var item in CoursesData)
{
viewModel.Add(new AssignedCourseData {
CourseID = item.CourseID,
CourseDescription = item.CourseDescription,
Assigned = false });
}
ViewBag.CoursePopulate = viewModel;
}
Это мнение
@{
int cnt = 0;
List<MySolution.ViewModels.AssignedCourseData> courses = ViewBag.CoursePopulate;
foreach (var course in courses)
{
<input type="checkbox" name="selectedCourse" value="@course.CourseID" />
@course.CourseDescription
}
}
И это тот контроллер, который получает данные (и где я хочу их сохранить).
Получается как параметрstring[] selectedCourse
для флажков:
[HttpPost]
public ActionResult Create(UserProfile userprofile, string[] selectedCourse)
{
if (ModelState.IsValid)
{
db.UserProfiles.Add(userprofile);
//TO DO: Save data from many to many (this is what I can't do!)
db.SaveChanges();
}
return View(userprofile);
}
EditЯ написал это в 3 постах с кодом
part 1 part 2 adds the courses and saves them with the user profile part 3 allows editing and deletion of users and their coursesGithub источник: https://github.com/cbruen1/mvc4-many-to-many
Я думаю, что вы немного отклонились от условностей, например, в некоторых из своих наименований, поэтому я внес изменения, которые я посчитал нужным. На мой взгляд, лучший способ опубликовать курсы как часть UserProfile - это сделать их с помощью шаблона редактора, который я объясню далее.
Вот как я бы все это реализовал:
(Спасибо @Slauma за указание на ошибку при сохранении новых курсов).
in your model, your "Courses" class is a single entity and would usually be named Course, and a collection of class Course would be named Courses. instead of having AssignedCourseData in the ViewBag use a view model implement this while creating a new user - i.e. have a standard Create view for a Userprofile containing an AssignedCourseData view model which will be posted back with the UserProfileViewModel.Начиная с БД, оставьте коллекцию UserProfile как есть и назовите коллекцию курсов Курсы:
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Course> Courses { get; set; }
В классе DbContext переопределите метод OnModelCreating. Вот как вы отображаете связь «многие ко многим» между UserProfile и Course:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProfile>()
.HasMany(up => up.Courses)
.WithMany(course => course.UserProfiles)
.Map(mc =>
{
mc.ToTable("T_UserProfile_Course");
mc.MapLeftKey("UserProfileID");
mc.MapRightKey("CourseID");
}
);
base.OnModelCreating(modelBuilder);
}
Я также добавил бы класс ложного инициализатора в том же пространстве имен, который даст вам несколько курсов для начала и означает, что вам не нужно добавлять их вручную каждый раз, когда ваша модель изменяется:
public class MockInitializer : DropCreateDatabaseAlways<MVC4PartialViewsContext>
{
protected override void Seed(MVC4PartialViewsContext context)
{
base.Seed(context);
var course1 = new Course { CourseID = 1, CourseDescripcion = "Bird Watching" };
var course2 = new Course { CourseID = 2, CourseDescripcion = "Basket weaving for beginners" };
var course3 = new Course { CourseID = 3, CourseDescripcion = "Photography 101" };
context.Courses.Add(course1);
context.Courses.Add(course2);
context.Courses.Add(course3);
}
}
Добавьте эту строку в Application_Start () Global.asax, чтобы запустить ее:
Database.SetInitializer(new MockInitializer());
Итак, вот модель:
public class UserProfile
{
public UserProfile()
{
Courses = new List<Course>();
}
public int UserProfileID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string CourseDescripcion { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
Теперь создайте 2 новых результата действия в вашем контроллере для создания нового профиля пользователя:
public ActionResult CreateUserProfile()
{
var userProfileViewModel = new UserProfileViewModel { Courses = PopulateCourseData() };
return View(userProfileViewModel);
}
[HttpPost]
public ActionResult CreateUserProfile(UserProfileViewModel userProfileViewModel)
{
if (ModelState.IsValid)
{
var userProfile = new UserProfile { Name = userProfileViewModel.Name };
AddOrUpdateCourses(userProfile, userProfileViewModel.Courses);
db.UserProfiles.Add(userProfile);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userProfileViewModel);
}
Вот ваши PopulateCourseData, аналогичные тем, которые у вас были, за исключением того, что они не вставлены в ViewBag - теперь это свойство в UserProfileViewModel:
private ICollection<AssignedCourseData> PopulateCourseData()
{
var courses = db.Courses;
var assignedCourses = new List<AssignedCourseData>();
foreach (var item in courses)
{
assignedCourses.Add(new AssignedCourseData
{
CourseID = item.CourseID,
CourseDescription = item.CourseDescripcion,
Assigned = false
});
}
return assignedCourses;
}
Создайте шаблон редактора. В папке Views \ Shared создайте новую папку с именем EditorTemplates, если у вас ее еще нет. Добавьте новое частичное представление с именем AssignedCourseData и вставьте приведенный ниже код. Это волшебство, которое правильно отображает и присваивает названия всем вашим флажкам - вам не нужен каждый цикл, так как шаблон Editor создаст все элементы, переданные в коллекции:
@model AssignedCourseData
@using MySolution.ViewModels
<fieldset>
@Html.HiddenFor(model => model.CourseID)
@Html.CheckBoxFor(model => model.Assigned)
@Html.DisplayFor(model => model.CourseDescription)
</fieldset>
Создайте модель представления профиля пользователя в папке моделей представлений - в ней есть коллекция объектов AssignedCourseData:
public class UserProfileViewModel
{
public int UserProfileID { get; set; }
public string Name { get; set; }
public virtual ICollection<AssignedCourseData> Courses { get; set; }
}
Добавьте новое представление CreateUserprofile.cshtml для создания профиля пользователя. Вы можете щелкнуть правой кнопкой мыши по уже добавленному методу контроллера CreateUserProfile и выбрать & quot; Добавить представление & quot ;:
@model UserProfileViewModel
@using MySolution.ViewModels
@using (Html.BeginForm("CreateUserProfile", "Course", FormMethod.Post))
{
@Html.ValidationSummary(true)
<fieldset>
@Html.DisplayFor(model => model.Name)
@Html.EditorFor(model => model.Name)
// Render the check boxes using the Editor Template
@Html.EditorFor(x => x.Courses)
</fieldset>
<p>
<input type="submit" value="Create" />
</p>
}
Это будет правильно отображать имена полей, чтобы они были частью модели представления профиля пользователя, когда форма отправляется обратно в контроллер. Поля будут названы так:
<fieldset>
<input data-val="true" data-val-number="The field CourseID must be a number." data-val-required="The CourseID field is required." id="Courses_0__CourseID" name="Courses[0].CourseID" type="hidden" value="1" />
<input data-val="true" data-val-required="The Assigned field is required." id="Courses_0__Assigned" name="Courses[0].Assigned" type="checkbox" value="true" /><input name="Courses[0].Assigned" type="hidden" value="false" />
Bird Watching
</fieldset>
Другие поля будут названы аналогично, за исключением того, что они будут проиндексированы с 1 и 2 соответственно. Наконец, вот как сохранить курсы в новом профиле пользователя, когда форма будет отправлена обратно. Добавьте этот метод в свой контроллер - он вызывается из результата действия CreateUserProfile, когда форма отправляется обратно:
private void AddOrUpdateCourses(UserProfile userProfile, IEnumerable<AssignedCourseData> assignedCourses)
{
foreach (var assignedCourse in assignedCourses)
{
if (assignedCourse.Assigned)
{
var course = new Course { CourseID = assignedCourse.CourseID };
db.Courses.Attach(course);
userProfile.Courses.Add(course);
}
}
}
Как только курсы становятся частью профиля пользователя, EF заботится об ассоциациях. Он добавит запись для каждого выбранного курса в таблицу T_UserProfile_Course, созданную в OnModelCreating. Вот метод результата действия CreateUserProfile, показывающий отправленные курсы:
Я выбрал 2 курса, и вы можете видеть, что курсы были добавлены в новый объект профиля пользователя: