我继承了我的第一个MVC项目,它涉及在Linq to SQL之上使用MVC3。我一直试图找到一种方法来生成一个复选框列表,该列表基于涉及交叉表的多对多关系。
我有一个systemFailureType表,它通过交叉表映射到SystemFailureProblem表。
这是表格的设计师布局:
这是我的模特:
[MetadataType(typeof(SystemFailureProblemMetadata))]
public partial class SystemFailureProblem
{
private class SystemFailureProblemMetadata
{
public int ID { get; set; }
[Required]
[StringLength(200)]
[DisplayName("Problem Description")]
public String Description { get; set; }
public IList<xSystemFailureProblemToType> FailureTypeCategories { get; set; }
}
}
[MetadataType(typeof(SystemFailureTypeMetaData))]
public partial class SystemFailureType
{
private class SystemFailureTypeMetaData
{
public int ID { get; set; }
[Required]
[StringLength(200)]
public String Description { get; set; }
}
}
我当前的视图代码使用包含问题对象的视图模型。因此,我生成复选框列表的当前代码如下所示:
@for(int i=0;i < Model.problem.FailureTypeCategories.Count(); i++)
{
@Html.CheckBox("FailureTypeCategories["+i+"].ID", false)
}
我的主要问题是,当我尝试生成表示FailureTypeCategories集合不存在的复选框列表时,我遇到了一些错误。我怀疑它可能与我目前如何设置模型有关。我最初的想法是倾向于实现交叉表的模型,虽然我不太确定如何整合它。我应该采用不同的方式来解决这个问题,还是我走在正确的轨道上而只是错过了一些东西?
编辑:
这是ViewModel
public SystemFailureProblem problem { get; set; }
public SystemFailureProblemViewModel() { }
public SystemFailureProblemViewModel(SystemFailureProblem problem)
{
this.problem = problem;
}
控制器方法非常简单。它只返回表单的部分视图。
public ActionResult Edit(int id)
{
try
{
return PartialView("Form", context.SystemFailureProblems.Single(p => p.ID == id));
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
return PartialView("Form", null);
}
}
答案 0 :(得分:1)
我提出了一个基于this article的想法,该想法利用了Entity Framework,但转换成LinqToSql类并不太难。
首先,我调整了ViewModel类。除了SystemFailureProblem
对象之外,您还需要在其中存储更多信息,例如与分配给该问题的SystemFailureType
集合相关的信息。
public class SystemFailureProblemTypeViewModel
{
public int TypeID { get; set; }
public string TypeDescription { get; set; }
public bool Assigned { get; set; }
}
接下来,我为Edit操作(GET和POST)创建了逻辑。在GET方法中,您可以找出当前为问题选择的类型(来自xSystemFailureProblemToType表),并使用该数据构造ViewModel。此ViewModel与SystemFailureProblem
对象一起传递给View。
public ActionResult Edit(int id)
{
SystemFailureProblem problem = (from p in context.SystemFailureProblems
where p.ID == id
select p).Single();
PopulateSystemFailureProblemData(problem);
return View(problem);
}
public void PopulateSystemFailureProblemData(SystemFailureProblem problem)
{
// get all failure types
var allTypes = from t in context.SystemFailureTypes select t;
// get al types joined with this problem using cross table
var problemTypes = from x in context.xSystemFailureProblemToTypes
join t in context.SystemFailureTypes on x.SystemFailureTypeID equals t.ID
where x.SystemFailureProblemID == problem.ID
select t;
// construct view model collection
List<SystemFailureProblemTypeViewModel> viewModel = new List<SystemFailureProblemTypeViewModel>();
foreach (var type in allTypes)
{
viewModel.Add(new SystemFailureProblemTypeViewModel
{
TypeID = type.ID,
TypeDescription = type.Description,
Assigned = problemTypes.Contains(type)
});
}
ViewBag.Types = viewModel;
}
在POST方法中,我们得到一个string []参数,告诉我们检查了哪些复选框。它是SystemFailureType
ID的列表。遍历数据库中的每个SystemFailureType
,确定选择/取消选择哪些,并相应地更新xSystemFailureProblemToType表。
[HttpPost]
public ActionResult Edit(int id, FormCollection collection, string[] selectedTypes)
{
SystemFailureProblem problem = (from p in context.SystemFailureProblems
where p.ID == id
select p).Single();
// get all types joined with this problem using cross table
var problemTypes = from x in context.xSystemFailureProblemToTypes
join t in context.SystemFailureTypes on x.SystemFailureTypeID equals t.ID
where x.SystemFailureProblemID == problem.ID
select t;
problem.FailureTypes = problemTypes.ToList<SystemFailureType>();
if (TryUpdateModel(problem, "", null, new string[] { "Types" }))
{
try
{
// loop through all types in the system
foreach (var failureType in context.SystemFailureTypes)
{
// determine if checkbox for current type was checked
if (selectedTypes.Contains(failureType.ID.ToString()))
{
// if no joining record exists (type not previously selected), create a joining record
if (!problemTypes.Contains(failureType))
{
context.xSystemFailureProblemToTypes.InsertOnSubmit(
new xSystemFailureProblemToType
{
SystemFailureProblemID = problem.ID,
SystemFailureTypeID = failureType.ID
});
}
}
else
{
// if type was unchecked but joining record exists, delete it
if (problemTypes.Contains(failureType))
{
xSystemFailureProblemToType toDelete = (from x in context.xSystemFailureProblemToTypes
where x.SystemFailureProblemID == problem.ID &&
x.SystemFailureTypeID == failureType.ID
select x).SingleOrDefault();
context.xSystemFailureProblemToTypes.DeleteOnSubmit(toDelete);
}
}
}
context.SubmitChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
PopulateSystemFailureProblemData(problem);
return View(problem);
}
最后,我调整了View。此代码将创建3列复选框,每个复选框的value属性为其代表的SystemFailureType
ID。
<div class="editor-field">
<table>
<tr>
@{
int cnt = 0;
List<SystemFailures.Data.SystemFailureProblemTypeViewModel> types = ViewBag.Types;
foreach (var type in types) {
if (cnt++ % 3 == 0) {
@: </tr> <tr>
}
@: <td>
<input type="checkbox"
name="selectedTypes"
value="@type.TypeID"
@(Html.Raw(type.Assigned ? "checked=\"checked\"" : "")) />
@type.TypeDescription
@:</td>
}
@: </tr>
}
</table>
</div>
它可能不是最有效的,但我认为它有效地解决了您问题中最复杂的部分。如果我遗漏了什么,请告诉我!