ASP.NET MVC 5 - строительные леса для отношений "один на один"
Я работаю над ASP.NET MVC 5, EF 6, Razor Engine, VB Language и база данных First подход с VS 2013.
Теперь в моей БД; У меня есть две таблицы, как показано ниже:
CREATE TABLE [dbo].[Group]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1, 1),
[Name] VARCHAR(50) NOT NULL
)
а также
CREATE TABLE [dbo].[Subscriber]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1, 1),
[FirstName] [nvarchar](100) NOT NULL,
[MiddleName] [nvarchar](100) NULL,
[LastName] [nvarchar](100) NOT NULL,
[Email] [varchar] (200) NOT NULL UNIQUE,
[GroupId] INT NULL REFERENCES [Group] ON DELETE SET NULL
)
Теперь, когда я автоматически генерирую контроллеры и представления с использованием Scaffolding; Я получаю элемент управления
Но на самом деле я хочу, чтобы представления "Создать группу" и "Редактировать группу" задали мне Subscribers
Я хочу добавить в определенную группу. Элемент управления HTML для того же самого может быть списком флажка или <выбрать несколько = "несколько"> со всеми Subscriber
элементы как
Как я могу создать / реализовать это?
1 ответ
Не полагайтесь слишком сильно на строительные леса. Все дело в том, что это дает вам основу для работы; это не главное, на ваш взгляд. Вы можете и должны модифицировать леса в соответствии с вашими потребностями, и, честно говоря, чаще всего легче начать с нуля, чем пытаться отменить весь ненужный пух, который добавляет леса.
Тем не менее, особенно при выборе нескольких связанных элементов одновременно, вам нужна модель представления. Попытка использовать вашу сущность для этого быстро иссякнет. Так что создайте класс как:
public class GroupViewModel
{
// `Group` properties you need to edit here
public List<int> SelectedSubscriberIds { get; set; }
public IEnumerable<SelectListItem> SubscriberChoices { get; set; }
}
Затем в вашем контроллере:
// We'll use this code multiple times so it's factored out into it's own method
private void PopulateSubscriberChoices(GroupViewModel model)
{
model.SubscriberChoices = db.Subscribers.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = m.FirstName + " " + m.LastName
});
}
public ActionResult Create()
{
var model = new GroupViewModel();
PopulateSubscriberChoices(model);
return View(model);
}
[HttpPost]
public ActionResult Create(GroupViewModel model)
{
if (ModelState.IsValid)
{
// Map the posted values onto a new `Group` instance. To set `Subscribers`,
// lookup instances from the database using the list of ids the user chose
var group = new Group
{
Name = model.Name,
Subscribers = db.Subscribers.Where(m => model.SelectedSubscriberIds.Contains(m.Id))
};
db.Groups.Add(group);
db.SaveChanges()
return RedirectToAction("Index");
}
PopulateSubscriberChoices(model);
return View(model);
}
public ActionResult Edit(int id)
{
var group = db.Groups.Find(id);
if (group == null)
{
return new HttpNotFoundResult();
}
// Map `Group` properties to your view model
var model = new GroupViewModel
{
Name = group.Name,
SelectedSubscriberIds = group.Subscribers.Select(m => m.Id).ToList()
};
PopulateSubscriberChoices(model);
return View(model);
}
[HttpPost]
public ActionResult Edit(int id, GroupViewModel model)
{
var group = db.Groups.Find(id);
if (group == null)
{
return new HttpNotFoundResult();
}
if (ModelState.IsValid)
{
group.Name = model.Name;
// Little bit trickier here
// First remove subscribers that are no longer selected
group.Subscribers.Where(m => !model.SelectedSubscriberIds.Contains(m.Id))
.ToList().ForEach(m => group.Subscribers.Remove(m));
// Now add newly selected subscribers
var existingSubscriberIds = group.Subscribers.Select(m => m.Id);
var newSubscriberIds = model.SelectedSubscriberIds.Except(existingSubscriberIds);
db.Subscribers.Where(m => newSubscriberIds.Contains(m.Id))
.ToList().ForEach(m => group.Subscribers.Add(m));
db.Entry(group).State = EntityState.Modified;
db.SaveChanges()
return RedirectToAction("Index");
}
PopulateSubscriberChoices(model);
return View(model);
}
Действие редактирования сообщения является наиболее сложным. Чтобы не получить ошибок о дубликатах ключей и т. Д., Необходимо убедиться, что вы не добавляете дубликаты в коллекцию. Вы также должны убедиться, что удалили связь между этой группой и любыми элементами, которые пользователь не выбрал. Кроме этого, это довольно просто.
Наконец, в ваших представлениях вам просто нужно отобразить список выбора:
@model Namespace.To.GroupViewModel
...
@Html.ListBoxFor(m => m.SelectedSubscriberIds, Model.SubscriberChoices)
ОБНОВИТЬ
Добавление преобразованного кода VB. Это может не работать на 100% из коробки. Любой, у кого есть опыт работы с VB, может свободно редактировать это, чтобы исправить любые проблемы.
Посмотреть модель
Public Class GroupViewModel
' Group properties you need to edit here
Public Property SelectedSubscriberIds As List(Of Integer)
Public Property SubscriberChoices As IEnumerable(Of SelectListItem)
End Class
Код контроллера
' We'll use this code multiple times so it's factored out into it's own method
Private Sub PopulateSubscriberChoices(model As GroupViewModel)
model.SubscriberChoices = db.Subscribers.[Select](Function(m) New SelectListItem With { _
.Value = m.Id, _
.Text = m.FirstName & " " & m.LastName _
})
End Sub
Public Function Create() As ActionResult
Dim model as New GroupViewModel
PopulateSubscriberChoices(model)
Return View(model)
End Function
<HttpPost> _
Public Function Create(model As GroupViewModel) As ActionResult
If ModelState.IsValid Then
' Map the posted values onto a new `Group` instance. To set `Subscribers`,
' lookup instances from the database using the list of ids the user chose
Dim group = New Group With { _
.Name = model.Name, _
.Subscribers = db.Subscribers.Where(Function(m) model.SelectedSubscriberIds.Contains(m.Id)) _
}
db.Groups.Add(group)
db.SaveChanges()
Return RedirectToAction("Index")
End If
PopulateSubscriberChoices(model)
Return View(model)
End Function
Public Function Edit(id As Integer) As ActionResult
Dim group = db.Groups.Find(id)
If group Is Nothing Then
Return New HttpNotFoundResult()
End If
' Map `Group` properties to your view model
Dim model = New GroupViewModel With { _
.Name = group.Name, _
.SelectedSubscriberIds = group.Subscribers.[Select](Function(m) m.Id).ToList _
}
PopulateSubscriberChoices(model)
Return View(model)
End Function
<HttpPost> _
Public Function Edit(id As Integer, model As GroupViewModel) As ActionResult
Dim group = db.Groups.Find(id)
If group Is Nothing Then
Return New HttpNotFoundResult()
End If
If ModelState.IsValid Then
group.Name = model.Name
' Little bit trickier here
' First remove subscribers that are no longer selected
group.Subscribers.Where(Function(m) Not model.SelectedSubscriberIds.Contains(m.Id)).ToList().ForEach(Function(m) group.Subscribers.Remove(m))
' Now add newly selected subscribers
Dim existingSubscriberIds = group.Subscribers.[Select](Function(m) m.Id)
Dim newSubscriberIds = model.SelectedSubscriberIds.Except(existingSubscriberIds)
db.Subscribers.Where(Function(m) newSubscriberIds.Contains(m.Id)).ToList().ForEach(Function(m) group.Subscribers.Add(m))
db.Entry(group).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
End If
PopulateSubscriberChoices(model)
Return View(model)
End Function