c# - 使用复杂的 View 模型和带有 Linq to SQL 的交叉表使用 MVC3 生成复选框列表

标签 c# asp.net-mvc-3 linq-to-sql normalization one-to-many

我继承了我的第一个 MVC 项目,它涉及在 Linq to SQL 之上使用 MVC3。我一直在尝试寻找一种方法来根据涉及交叉表的多对多关系生成复选框列表。

我有一个 systemFailureType 表,它通过交叉表映射到 SystemFailureProblem 表。

这是我设计的表格布局:

Designer Layout 这是我的模型:

     [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/

相关文章:

c# - WPF 中的平滑 ProgressBar

c# - 如何在 WPF RichTextBox 中突出显示数字

c# - 如何使用 linq-to-sql 同时增加页面浏览量?

c# - Parallel.Foreach 和每个产生不同的结果 : Why is my code unsafe?

c# - 具有共享区域/内存的 C++ 应用程序和 C# 应用程序

c# - 命令、命令处理程序和命令调用程序

c# - 使用 C# 创建缩略图

c# - 将依赖项注入(inject) ASP.NET MVC 3 操作过滤器。这种方法有什么问题?

c# - 使 "compute intermediate results in a temp table"SQL 模式适应 LINQ?

linq-to-sql - linq的where子句中的switch case