在Context中播种反序列化的JSON数据,然后尝试返回DbSet。 api / module返回带有空ICollection的所有Modules(如果我不实例化它们,则返回null),在评估过程中,愉快地返回虚拟Module。
以前在MVC中的经验,据此我可以在将对象发送到视图之前对其进行访问,因此我以前从未遇到过此问题。
注释掉的行:
//Enumerable.ToList(ModuleItems.Include(mi => mi.Assessments));
解决了这个问题,但感觉很棘手,并且需要为每个DbSet重复操作。在存储库中进行调用时,将模型名称作为参数传递给它们以包括它们也感觉很hack。
最佳做法是什么?
编辑:要添加的内容是,当我在填充ICollection的种子时检查DbSet时,随后在评估DbSet中包含6个项目。
模块
public class Module
{
[Key]
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public DateTime InstanceStartDate { get; set; }
public DateTime InstanceEndDate { get; set; }
public ICollection<UnitLeaderModules> UnitLeaderModules { get; set; } = new HashSet<UnitLeaderModules>();
public ICollection<Assessment> Assessments { get; set; } = new HashSet<Assessment>();
}
评估
public class Assessment
{
[Key]
public int Id { get; set; }
[ForeignKey("Module")]
public int ModuleId { get; set; }
public string Description { get; set; }
public DateTime SubmissionDateMain { get; set; }
public DateTime SubmissionDateResit { get; set; }
public string SubmissionMethod { get; set; }
public virtual Module Module { get; set; }
}
通用存储库
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext Context;
protected DbSet<T> DbSet;
public Repository(DbContext context)
{
Context = context;
DbSet = context.Set<T>();
}
public T Get<TKey>(TKey id)
{
return DbSet.Find(id);
}
public IQueryable<T> GetAll()
{
return DbSet;
}
public IQueryable<T> GetWhere(Expression<Func<T, bool>> whereExpression)
{
return DbSet.Where(whereExpression);
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
Save();
}
public void Update(T entity)
{
Save();
}
private void Save()
{
Context.SaveChanges();
}
}
模块控制器
[Route("api/module")]
[ApiController]
public class ModuleController : ControllerBase
{
private readonly IRepository<Module> _repository;
public ModuleController(IRepository<Module> repository)
{
_repository = repository;
}
[HttpGet]
public ActionResult<IQueryable<Module>> GetAll()
{
return Ok(_repository.GetAll());
}
[HttpGet("{id}", Name = "GetModule")]
public ActionResult<Module> GetById(int id)
{
var item = _repository.Get(id);
if (item == null)
{
return NotFound();
}
return item;
}
}
上下文
public class UnitLeaderContext : DbContext
{
public DbSet<Leader> UnitLeaderItems { get; set; }
public DbSet<UnitLeaderModules> UnitLeaderModuleItems { get; set; }
public DbSet<Module> ModuleItems { get; set; }
public DbSet<Assessment> AssessmentItems { get; set; }
public UnitLeaderContext(DbContextOptions<UnitLeaderContext> options)
: base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
if (!EnumerableExtensions.Any(ModuleItems))
{
var data =
@"[
{
""id"": 1,
""code"": ""YEP404"",
""description"": ""Marine Systems"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 1,
""moduleId"": 1,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-01-15T00:00:00"",
""submissionDateResit"": ""2019-07-06T00:00:00"",
""submissionMethod"": ""Upload""
},
{
""id"": 2,
""moduleId"": 1,
""description"": ""Examination (40%)"",
""submissionDateMain"": ""2019-03-28T00:00:00"",
""submissionDateResit"": ""2019-07-08T00:00:00"",
""submissionMethod"": ""Email Lecturer""
}
]
},
{
""id"": 2,
""code"": ""EEN402"",
""description"": ""Marine Production"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 3,
""moduleId"": 2,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""SOL""
},
{
""id"": 4,
""moduleId"": 2,
""description"": ""Log Book 1 (40%)"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""SOL""
}
]
},
{
""id"": 3,
""code"": ""YEP402"",
""description"": ""Marine Materials"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 5,
""moduleId"": 3,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-03-15T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""Hand-in Office""
},
{
""id"": 6,
""moduleId"": 3,
""description"": ""Examination"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""In-person Exam""
}
]
}
]
";
var aaa = JsonConvert.DeserializeObject<List<Module>>(data);
ModuleItems.AddRange(aaa);
SaveChanges();
}
//Enumerable.ToList(ModuleItems.Include(mi => mi.Assessments));
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Module>().HasKey(m => m.Id);
builder.Entity<Module>().HasMany(m => m.UnitLeaderModules);
builder.Entity<Module>().HasMany(m => m.Assessments);
builder.Entity<Assessment>().HasKey(m => m.Id);
builder.Entity<Assessment>().HasOne(m => m.Module);
}
}
答案 0 :(得分:0)
[取消包含]解决了该问题,但感觉很hacky,因此需要为每个DbSet重复
这不是黑客。每个控制器定义其返回数据的形状,即客户端是否应仅获取模块或同时获取所有评估。并且在DbSet上使用Include是Controller指定所需数据形状的方式。
您的JSON序列化程序可能最终会触发某些导航属性的延迟加载,但是当序列化为JSON时,您应该禁用延迟加载并显式构建对象图。
答案 1 :(得分:0)
您应该考虑避免在服务器和客户端之间传递实体。通过将这些引用声明为非虚拟引用,它们将不会按需延迟加载。从控制器和JSON序列化的角度来看,您无论如何都不想触发延迟加载,因为这会降低性能。因此,您需要急于加载要包含在客户端中的所有子引用。但是,由于许多原因,这是一个坏主意,
性能-您是否需要该实体及其所有子代的所有资产?通过传递实体,您需要数据库加载所有列,通过网络将其传输到应用服务器,为所有数据分配内存,然后通过网络将其传输到客户端,并在浏览器上为该状态分配内存。真的需要吗?如果启用了延迟加载,则序列化可能会使延迟加载代理调用失败,并导致性能下降。通常有理由认为传递实体是为了避免在更新时进行额外的读取调用。但是,大多数系统读取的内容远远超过写入的内容。更快地进行读取胜过编写时可能节省的额外读取。
完整性-您是否渴望加载每个儿童参考?今天,您可能需要一组孩子,并且不急于加载所有内容以帮助最小化第1点。但是,明天某人可能会看一下代码,并假设他们有一个完整的实体图可以使用。它还不完整,这意味着潜在的错误和更多的包含物,从而降低性能。
复杂性和可伸缩性-加载实体并将其发送给客户端,然后让客户端修改该实体,将其发送回服务器,将其附加到DbContext,然后保存更改,看起来很简单。无需两次加载实体。除了重新附加实体图是凌乱的,而且那个实体的数据在那时可能已经改变了。在某些系统上,“最后取胜”是可以的,但如果用户不希望这样,则可能会出现问题。
安全性-您是否将所有这些数据公开给所有用户?您的UI可能会限制用户可以查看或编辑的数据,但是,如果您的结构绕过实体,并且您倾向于仅将发送回的实体附加到服务器上,并保存上下文,那么您将向骇客风险。聪明的用户可以使用浏览器上的调试工具,以完整的状态查看发送到客户端的数据。他们还可以修改对服务器的响应,以更改否则将无法编辑的数据,甚至可能更改FK引用以影响他们无权查看或更改的数据。您可以通过在提交之前对数据库进行检查来减轻这种情况,但是您的实体不过是超重视图模型/ DTO。
使用通过Select
从EF Linq表达式中实现的映射视图模型有助于解决所有这些情况。像Automapper这样的工具可以减少执行映射的“无聊”额外代码。通过使用视图模型:
对于任何在传递周围事物时遇到问题的人来说,都是值得深思的。