将我的 ICollection 覆盖为我的 edmx 文件中的 IList 的最佳方法,在 asp.net MVC Web 应用程序中

Posted

技术标签:

【中文标题】将我的 ICollection 覆盖为我的 edmx 文件中的 IList 的最佳方法,在 asp.net MVC Web 应用程序中【英文标题】:Best appraoch to override my ICollection to be IList inside my edmx file, inside asp.net MVC web application 【发布时间】:2017-11-11 15:24:51 【问题描述】:

我正在开发一个 ASP.NET MVC Web 应用程序。我已经使用 ADO.NET Entity Framework 映射了我的数据库表,它生成了一个.edmx 文件。现在生成的.edmx 文件的限制之一是所有代表父子关系的集合都将被定义为ICollection。例如我有这个问题模型类:-

public Question()
        
            this.Anwers = new HashSet<Anwer>();
        

        public int Id  get; set; 
        public string QuestionDesc  get; set; 
        public int Order  get; set; 
        public bool Active  get; set; 

        public virtual ICollection<Anwer> Answers  get; set; 

这是答案模型:-

  public partial class Anwer
    
        public Anwer()
        
            this.UserFormsAnswers = new HashSet<UserFormsAnswer>();
        

        public int Id  get; set; 
        public string AnwerDesc  get; set; 
        public int QuestionID  get; set; 
        public bool Correct  get; set; 

        public virtual Question Question  get; set; 
        public virtual ICollection<UserFormsAnswer> UserFormsAnswers  get; set; 
    

现在ICollectionIList&lt;&gt; 相比的问题是我无法使用索引器来获取与问题相关的答案的值。如果我有IList&lt;&gt; 而不是ICollection&lt;&gt;,我可以索引问题的答案。

所以我的问题是将我的public virtual ICollection&lt;Anwer&gt; Answers get; set; 覆盖为public virtual IList&lt;Anwer&gt; Answers get; set; 的最佳方法是什么?当然应该避免修改自动生成的.edmx 文件,因为如果我重新映射我的数据库表,我的修改将被覆盖..

【问题讨论】:

你能分享你的答案模型吗 @hasan ok 更新了。 是否需要索引仍然值得怀疑。在您的示例中,@for 可以轻松替换为@foreah,从而消除了索引的需要。 EF 模型集合导航属性根据定义是无序的,因此 ICollection&lt;T&gt;IList&lt;T&gt; 更适合(从生成的代码中可以看出,它允许实现使用更高效的 Add / Remove / Contains HashSet&lt;T&gt; 类提供的实现,如果需要暴露IList&lt;T&gt;,则无法实现。 您正在编辑数据,因此您永远不应该在视图中使用您的数据模型。规则 1:始终使用视图模型(并且视图模型包含 IList&lt;T&gt; 属性)。另请注意,您可以使用与IEnumerable&lt;T&gt; 一起使用的自定义EditorTemplate,并且不需要IList&lt;T&gt; - 请参阅this answer,但您不能使用@IvanStoev 建议的foreach 循环 暂时忘记讨论为什么以及是否需要公开IList 而不是ICollection。当前答案的第一部分提供了您问题的直接解决方案 - *modify T4 templates 用于从edmx 生成实体模型类。你还需要什么——究竟如何修改.tt文件,要修改哪个.tt文件? 【参考方案1】:

如果您确实需要将所有ICollection 对象修改为IList 对象,那么执行此操作的正确位置是在您的T4 模板中。它们是 edmx 中的“.tt”文件。这些是用于生成项目其余部分使用的代码文件的模板。每当您更新 edmx 时,对这些模板所做的任何修改都会在您的项目中传播。

也就是说,我会质疑您是否真的需要进行此更改。最好让数据库的返回类型尽可能通用,然后将其缩小到要使用它的 List(或其他)。

【讨论】:

@Nesoras 现在我提到使用ICollection 实现外键,将不允许我应用索引.. 而使用代码优先方法将外键关系实现为List 或父对象上的ILIST .. 所以我认为使用LIST 而不是ICollection 是错误的,因为在代码优先的方法中,所有FK 都将在父对象内实现为ILIST 您能就此提出建议吗? @johnG 答案的第一部分是涵盖如何做你想做的事。我看不出你还需要什么。 @johnG 正如我在最初的回答中所说,如果您希望返回类型永久不同,则可以修改 T4 模板。至于为什么要将其保留为ICollection,请考虑您希望在Queue&lt;T&gt; 中使用EF 返回的数据的可能性。由于Queue&lt;T&gt; 实现了ICollection 而不是IList,因此在对T4 模板进行更改后您将无法这样做。最好将 EF 返回的数据保留为 ICollection,然后在您需要使用它的集合上调用 .ToList(),例如在您上面引用的前端视图模型中。 听起来您将视图直接绑定到由 edmx 创建的模型对象。这就是你问题的根源所在。相反,您应该有一个 Question 对象,该对象由您的 edmx 创建,上面有一个 ICollection&lt;Answer&gt;。然后在你的前端项目中你应该有一个QuestionViewModel 对象,上面有一个IList&lt;AnswerViewModel&gt;。然后,您可以将数据库模型投影到视图模型上,并在表示层使用视图模型数据类型,而不是用表示问题污染您的数据层。【参考方案2】:

ToList您的收藏并使用 List.. 它实现了 IList 并具有索引器方法 []

例如

@for (var i = 0; i < m.Answers.Count; i++)
     @html.EditorFor(m => m.Anwers.ToList()[i].Id)
 

当然,这个例子很愚蠢,因为每次 for 循环都会强制转换为列表。但是您可以在 for 循环之前轻松地将答案列在列表中。最好的办法是使用视图模式。我可以发布看起来像的代码。你可以先用剃须刀做这样的事情

 @List<Answers> answers =  m.Anwers.ToList()
 @for (var i = 0; i < answers.Count; i++)
         @Html.EditorFor(answers => answers[i].Id)
  

顺便说一句,你绝对可以用 foreach 循环和 iCollection 做同样的事情。

【讨论】:

【参考方案3】:

在 IList 中是派生自 ICollection。在 EDMX 文件生成的模型中 首先我们改变

public Question()
    
        this.Anwers = new HashSet<Anwer>();
    
    public int Id  get; set; 
    public string QuestionDesc  get; set; 
    public int Order  get; set; 
    public bool Active  get; set; 
    public virtual ICollection<Answer> Answers  get; set; 

首先我们需要改变 Hashset&lt;Answer&gt; to List&lt;Answer&gt;()

public virtual ICollection&lt;Answer&gt; Answers get; set;

public virtual IList&lt;Answer&gt; Answers get; set;

如果我们改变了它就可以正常工作

Sample Image With ICollection to IList Change

Sample Image for Fetching Record With Indexer with out Exception

在 NHibernate ORM 中,使用 IList 替代 ICollection 进行关系, 使用前请检查以下链接

Why use ICollection and not IEnumerable or List on many-many/one-many relationships?

如下更新 Model.tt 文件

将 ICollection 更改为 IList

navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("IList<" + endType + ">") : endType, 

HashSet 改为 List

this.<#=code.Escape(navigationProperty)#> = new List<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); 

一旦您像上面一样更新了 model.tt 文件,从 DB 重新映射 EDMX 更新时不会出现任何问题。

【讨论】:

下次他重新生成他的 EDMX 文件时会发生什么?

以上是关于将我的 ICollection 覆盖为我的 edmx 文件中的 IList 的最佳方法,在 asp.net MVC Web 应用程序中的主要内容,如果未能解决你的问题,请参考以下文章

如何将我的 API 中的数据设置为我的反应组件的初始状态?

无法将我的 mat4 转换矩阵解析为我的 opengl 着色器?

如何将我的 SwiftUI 视图的 @State 交换为我的视图模型 @Published 变量?

EF 6 codde first - 一对多映射 - ICollection始终为null

将我的 JSON 插入到集合中时,MongoDB 会覆盖我的自定义 id 属性吗?

为我的应用程序之外的单个模型覆盖 django 管理员更改表单?