注意:因为数据库中的 ID 看起来很完美,所以当我尝试引用模型属性的属性时,可能只是我的 razor View 中的错误(在帖子的末尾)。我不擅长 LINQ。
我在 class Person
中有一个 Models.Person.cs
,它引用了 Surveys:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace WebApplication2.Models {
public class Person {
public int Id { get; set; }
// MANY PROPERTIES BEETWEEN
[DisplayName("Surveys")]
public virtual ICollection<Survey> Surveys { get; set; }
}
}
我得到了 class Survey
,它由 Question
组成,每个 Question
都有一个 Answer.
These classes are in
Models.Survey.cs`,看起来像:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication2.Models {
public class Question {
public int Id { set; get; }
public string QuestionText { set; get; }
public ICollection<Answer> Answers { set; get; }
public string SelectedAnswer { set; get; }
public virtual Survey Survey { get; set; }
}
public class Answer {
public int Id { set; get; }
public string AnswerText { set; get; }
public virtual Question Question { get; set; }
}
public class Survey {
public int Id { set; get; }
public ICollection<Question> Questions { set; get; }
public virtual Person Person { get; set; }
}
}
Entity Framework 数据库上下文类如下所示:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false) {
System.Diagnostics.Debug.WriteLine("CONSTRUCTOR");
}
public DbSet<Person> Persons { get; set; }
public DbSet<Meeting> Meetings { get; set; }
public DbSet<Status> Statuses { get; set; }
public DbSet<Survey> Surveys { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Answer> Answers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public static ApplicationDbContext Create() {
return new ApplicationDbContext();
}
}
我为数据库播种:
protected override void Seed(WebApplication2.Models.ApplicationDbContext context) {
System.Diagnostics.Debug.WriteLine("SEED STARTED");
var survey = new Survey() { Questions = new List<Question>() };
if (!context.Surveys.Any()) {
//the below is hardcoded for DEMO. you may get the data from some
//other place and set the questions and answers
var q1 = new Question() { QuestionText = "What is your favourite language", Answers = new List<Answer>() };
Answer a1 = new Answer() { AnswerText = "PHP" };
Answer a2 = new Answer() { AnswerText = "ASP.NET" };
Answer a3 = new Answer() { AnswerText = "Java" };
context.Answers.AddOrUpdate(a1);
context.Answers.AddOrUpdate(a2);
context.Answers.AddOrUpdate(a3);
context.Questions.AddOrUpdate(q1);
q1.Answers.Add(a1);
q1.Answers.Add(a2);
q1.Answers.Add(a3);
survey.Questions.Add(q1);
var q2 = new Question() { QuestionText = "What is your favourite DB", Answers = new List<Answer>() };
a1 = new Answer() { AnswerText = "SQL Server" };
a2 = new Answer() { AnswerText = "MySQL" };
a3 = new Answer() { AnswerText = "Oracle" };
q2.Answers.Add(a1);
q2.Answers.Add(a2);
q2.Answers.Add(a3);
context.Answers.AddOrUpdate(a1);
context.Answers.AddOrUpdate(a2);
context.Answers.AddOrUpdate(a3);
context.Questions.AddOrUpdate(q2);
survey.Questions.Add(q2);
context.Surveys.AddOrUpdate(survey);
context.SaveChanges();
}
if (!context.Persons.Any()) {
var persons = new List<Person> {
new Person{FirstName = "John", LastName = "Doe", CellNumber = "123-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "312312312", Notes = "Annoying", Surveys = new List<Survey>(){survey}},
new Person{FirstName = "Anna", LastName = "Doe", CellNumber = "113-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "548555672", Notes = "Less Annoying", Surveys = new List<Survey>()}
};
persons.ForEach(person => context.Persons.AddOrUpdate(person));
context.SaveChanges();
}
}
生成的数据库看起来很不错。我们可以看到 Survey(Id=4) 与 John Doe(Id=6) 相关联,而 Questions(Id=7,8) 与 Survey(Id=4) 等相关联。所以它看起来很棒。强>
但是 如果我尝试显示(在 Razor View 中)John Doe(Id = 6) 的调查数量,我得到 1,这是正确的,然后是该调查中的问题数量(由 @Html.DisplayFor(x => survey.Questions.Count
) 我得到 A first chance exception of type 'System.NullReferenceException' occurred in System.Web.Mvc.dll
但没有显示任何内容。
观点:
@model WebApplication2.Models.Person
<hr />
<h2>@Html.DisplayNameFor(model => model.Surveys)</h2>
<input type="button" id="Coll" value="Collapse" onclick="javascript:CollapseDiv()" />
<p>
@Html.DisplayFor(x => Model.Surveys.Count)
</p>
@foreach (var survey in Model.Surveys) {
<p>
@using (Html.BeginForm()) {
<text>ANK</text>
@Html.DisplayFor(x => survey.Questions.Count) //HERE I GET NULL POINTER EXCEPTION
<input type="submit" />
}
</p>
}
<hr />
结果:
编辑:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().HasMany(p => p.Answers).WithMany(a => a.Persons);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}`
最佳答案
尝试在 Questions
集合中添加虚拟关键字。只要上下文仍然存在,这将启用延迟加载。您可能还需要将其添加到 Answers
集合中。
public virtual ICollection<Question> Questions { set; get; }
或者您可以使用预先加载。
var model = db.Persons.
Include(p => p.Questions).FirstOrDefault(p => p.Id == xx);
附言
加载导航属性的方法有几种。
延迟加载
必填
virtual
导航属性上的关键字- 启用延迟加载,
db.Configuration.LazyLoadingEnabled = true;
- 启用代理,
db.Configuration.ProxyCreationEnabled = true;
DbContext
未释放,如果释放,将抛出对象释放异常- -
只有在访问该属性时才会加载相关导航。例如:
// Questions is not loaded yet. var person = db.Persons.FirstOrDefault(); // Questions will be loaded. var questions = person.Questions; // Questions will be loaded even though you only ask for the count. var qCount = person.Questions.Count;
预加载
必需
包含
方法- -
这将在生成的查询中使用连接语句加载导航属性作为主查询的一部分。
// Questions will be loaded together with the main query. var person = db.Persons.Include(p => p.Questions).FirstOrDefault();
显式加载
必需
db.Entry(entity).Reference(/* reference */).Load();
方法db.Entry(entity).Collection(/* collection */).Load();
方法DbContext
- -
这将显式加载每个导航属性。
// Loads the main entity first. var person = db.Persons.FirstOrDefault(); // Explicitly load the Questions. db.Entry(person).Collection(p => p.Questions).Load();
此外,如果需要,您可以过滤
问题
。db.Entry(person) .Collection(p => p.Questions) .Query() .Where(q => q.QuestionText.Contains("Entity Framework")) .Load();
关系修正
必需
Select
匿名类型中每个导航属性的子句- -
这将自动链接主实体和导航属性,也可用于过滤导航属性。生成的查询将生成连接语句。
var person = db.Persons.Select(p => new { Person = p, Questions = p.Questions .Where(q => q.QuestionText.Contains("Entity Framework")) }).AsEnumerable().Select(t => t.Person).FirstOrDefault();
上面的查询在第一个
Select
子句中将Person
和Questions
选择为匿名类型,然后将其转换为可枚举类型(延迟执行)和然后第二个Select
子句只选择Person
属性,但结果会将Questions
属性链接到每个Person
.最后通过FirstOrDefault
方法执行查询。
如果没有上述任何方法,导航属性将不会自动加载。
更多:
关于mysql - 属性为 null 导致 NullReferenceException 但 Entity Framework 数据库中的主键和外键是正确的并且彼此完全匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25467443/