我继承了我的第一个 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; }
}
}
我当前的 View 代码使用包含问题对象的 View 模型。因此,我用于生成复选框列表的当前代码如下所示:
@for(int i=0;i < Model.problem.FailureTypeCategories.Count(); i++)
{
@Html.CheckBox("FailureTypeCategories["+i+"].ID", false)
}
我的主要问题是,当我尝试生成表明 FailureTypeCategories 集合不存在的复选框列表时,我遇到了一些错误。我怀疑这可能与我当前设置模型的方式有关。我最初的想法倾向于为交叉表实现一个模型,尽管我不太确定我将如何整合它。我应该采用不同的方式来解决这个问题,还是我走在正确的轨道上只是遗漏了什么?
编辑:
这是 View 模型
public SystemFailureProblem problem { get; set; }
public SystemFailureProblemViewModel() { }
public SystemFailureProblemViewModel(SystemFailureProblem problem)
{
this.problem = problem;
}
Controller 方法非常简单。它只返回表单的部分 View 。
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);
}
}
最佳答案
我想出了一个基于 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; }
}
接下来,我为编辑操作(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 列复选框,每个复选框的值属性是它代表的 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>
它可能不是最有效的,但我认为它有效地解决了您问题中最复杂的部分。如果我遗漏了什么,请告诉我!
关于c# - 使用复杂的 View 模型和带有 Linq to SQL 的交叉表使用 MVC3 生成复选框列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8915750/