C# 什么';更新父模型/属性中的子模型/属性的最佳/最有效方法是什么?
假设我有C# 什么';更新父模型/属性中的子模型/属性的最佳/最有效方法是什么?,c#,asp.net-mvc,asp.net-core,.net-core,asp.net-core-mvc,C#,Asp.net Mvc,Asp.net Core,.net Core,Asp.net Core Mvc,假设我有ClassA,它具有以下属性: //ClassA int ID string Name ClassB SubModel 我还有ClassB,它具有以下属性: //ClassB int ID string Name 现在我有了一个视图,用户可以在其中修改ClassA以及ClassA关联的ClassB属性 修改ClassB的最佳方式是什么 现在,当我检索ClassA时,我执行.Include来检索它的ClassB,当我向用户公开属性时,我执行ClassA.SubModel.Name等操作
ClassA
,它具有以下属性:
//ClassA
int ID
string Name
ClassB SubModel
我还有ClassB
,它具有以下属性:
//ClassB
int ID
string Name
现在我有了一个视图,用户可以在其中修改ClassA
以及ClassA
关联的ClassB
属性
修改ClassB
的最佳方式是什么
现在,当我检索ClassA
时,我执行.Include
来检索它的ClassB
,当我向用户公开属性时,我执行ClassA.SubModel.Name
等操作
这是正确的方法吗?或者我应该分别获取ClassA
和ClassB
,并将它们作为两个单独的属性,当我更新时,调用两个更新来分别更新它们吗?最佳实现这一点的方法非常主观,但实现这一点的一种常用方法是使用。在检索视图所需的数据时包括(正如您在上面提到的那样),然后在视图中可以使用Html帮助程序和表单post,如下图所示,将编辑后的数据发布回控制器:
@model MyApplication.ViewModels.ClassA
@using (Html.BeginForm("Edit", "MyController", FormMethod.Post, new { @class = "form-horizontal", role = "form"))
{
@Html.EditorFor(x => x.Name)
@Html.EditorFor(x => x.SubModel.Name)
<input type="submit" value="Save" />
}
@model MyApplication.ViewModels.ClassA
@使用(Html.BeginForm(“Edit”、“MyController”、FormMethod.Post、new{@class=“form horizontal”、role=“form”))
{
@EditorFor(x=>x.Name)
@EditorFor(x=>x.SubModel.Name)
}
HTML助手将帮助您正确格式化属性的HTML名称属性,以便数据从视图正确序列化到控制器。我认为没有一种“最佳”方法可以做到这一点。这取决于您的业务。因此,我只想分享我将如何做到这一点 我的DDD方法 在我的Domain Drive Design practice项目中,我有两组模型,不包括视图的视图模型:
- 域模型-表示您的业务逻辑,而不依赖于您的基础架构代码
- 持久性模型-表示如何在所选数据库中映射域模型
ClassA
和ClassB
定义为具有一对多关系的实体
假设我使用entityframeworkcore
作为ORM,使用sqlserver
作为数据库
public class ClassAEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int ClassBId { get; set; }
public ClassBEntity ClassB { get; set; }
}
public class ClassBEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<ClassAEntity> ClassAs { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options): base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Configure relationships
builder.Entity<ClassAEntity>(b =>
{
b.HasKey(x => x.Id);
b.Property(x => x.Name).IsRequired();
b.HasOne(x => x.ClassB)
.WithMany(y => y.ClassAs)
.HasForeignKey(x => x.ClassBId);
b.ToTable("ClassA");
});
builder.Entity<ClassBEntity>(b =>
{
b.HasKey(x => x.Id);
b.Property(x => x.Name).IsRequired();
b.ToTable("ClassB");
});
}
public DbSet<ClassAEntity> ClassAs { get; set; }
public DbSet<ClassBEntity> ClassBs { get; set; }
}
希望您能看到使用类来表示业务逻辑的好处。您可以在其中使用私有setter来防止其他类更改数据。您可以定义对其他类开放的方法,这些方法具有适当的验证
编辑屏幕
由于我的ClassA
和ClassB
是两个单独的聚合,我不打算同时用屏幕来更新它们。相反,我有两个单独的控制器来表示它们
在编辑ClassA
屏幕上,我可以提供可用classb
的下拉列表,因为它们是一对多的关系
同样,没有正确的方法。这完全取决于您的业务逻辑
public ClassAController : AdminControllerBase
{
private readonly AppDbContext _dbContext;
public ClassAController(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult Edit(int id)
{
// Find the entity by id in the database
var classAEntity = _dbContext.ClassAs
.AsNoTracking()
.SingleOrDefault(x => x.Id == id);
if (classAEntity == null)
{
return NotFound();
}
// Find a list of available class Bs
var availableClassBs = _dbContext.ClassBs
.AsNoTracking()
.Where(x => ... your filter ...)
.OrderBy(x => x.Name)
.ToDictionary(x => x.Id, x => x.Name);
// Construct the view model for editing
var vm = new EditClassAViewModel
{
ClassAId = classAEntity.Id,
Name = classAEntity.Name,
SelectedClassBId = classAEntity.ClassBId,
AvailableClassBs = availableClassBs
};
return View(vm);
}
}
这是编辑屏幕的视图模型。根据您想要编辑的内容,您可以为此构造属性。如果您启用了客户端验证,这也是放置数据批注的正确位置
public class EditClassAViewModel
{
[Required]
public int ClassAId { get; set; }
[Required]
public string Name { get; set; }
[Display(Name = "Class b")]
public int SelectedClassBId { get; set; }
public IDictionary<int, string> AvailableClassBs { get; set; }
}
免责声明:是的,我知道我在这篇文章中可能忽略了很多新东西,例如,使用IMeditor的请求/命令模式、使用FluentValidation的验证、AutoMapper配置和存储库模式。但这篇文章的重点只是让你了解我的DDD方法。XD是什么让你认为只有一个“最佳”呢方法?您可以创建一个新的
ClassC
,它将是该操作/视图的模型,它将是ClassA
和ClassB
的合并。这样,您只需在视图中处理一个平面类,就可以在控制器中转换/映射到您的持久类。查找POCO类。
public class EditClassAViewModel
{
[Required]
public int ClassAId { get; set; }
[Required]
public string Name { get; set; }
[Display(Name = "Class b")]
public int SelectedClassBId { get; set; }
public IDictionary<int, string> AvailableClassBs { get; set; }
}
[HttpPost]
public IActionResult Edit(EditClassAViewModel model)
{
var response = new JsonResponse();
if (!ModelState.IsValid)
{
response.AddModalStateErrors(ModelState);
return Json(response);
}
// Get the ClassA entity from the database and convert the persistence
// model to your domain model. You could have your repository to do
// both in one step.
var classAEntity = _dbContext.ClassAs
.AsNoTracking()
.SingleOrDefault(x => x.Id == model.ClassAId);
if (classAEntity == null)
{
response.AddError(...);
return Json(response);
}
// Convert the persistence model to domain model. You could use
// AutoMapper to do so.
var classA = new ClassA(...);
// Class the ClassA domain model UpdateDetails method
classA.UpdateDetails(...);
// Convert the domain model back to persistence model
// and save it to the database. You could have your repository to do
// both in one step.
var classAPersistenceModel = ...;
// Since this persistence model is not tracked by EFCore,
// you need to fetch the entity again from database by Id and update
// that entity instead.
// Again, you could have your repository to do that in one step too.
classAEntity = _dbContext.ClassAs.Find(classAPersistenceModel.Id);
if (classAEntity != null)
{
_dbContext.Entry(classAEntity).CurrentValues
.SetValues(classAPersistenceModel);
_dbContext.SaveChanges();
}
}