使用不同实体的viewModel保存表单中的输入



我有一个视图Model,它显示了我表单中需要的所有信息。

正如您在下面看到的,我的ViewModel使用CustomerEntities。我想将信息保存在使用CalendarEntities的表中。我曾想过让日历在Customer之外有自己的实体,但我需要使用CustomerEntities从登录用户那里检索信息以放入表单。当我将项目放入HttpPost时,它表示CustomerEntities不包括事件的定义。在不将事件从自己的实体转移到CustomerEntity的情况下,是否可以绕过这一问题?

这是CalendarController

private CalendarEntities customer = new CalendarEntities();
private CustomerEntities db = new CustomerEntities();
// GET: ServiceCalendar
public ActionResult Index(string UserId)
{
if (UserId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
AddEventViewModel modelInstance = AddEventViewModel.GetCustomerInfo(UserId, db);
return View(modelInstance);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "CustomerId,CustomerName,AllDay,Location,Title,Description,StartDate,EndDate")] EventModels.Entities.Events calendarEvent)
{
if (ModelState.IsValid)
{
db.Events.Add(calendarEvent);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(calendarEvent);
}

这是视图型号

public class AddEventViewModel
{
public static AddEventViewModel GetCustomerInfo(string userId, CustomerEntities db)
{
var QCustInfo = from ad in db.Addresses
join ua in db.UserToAddresses on ad.AddressId equals ua.AddressId
join cus in db.CustomerNames on ad.CustomerId equals cus.CustomerId
where (ua.UserId == userId)
select new AddEventViewModel
{
CustomerId = cus.CustomerId,
CustomerName = cus.CustomerName,
//Location = ad.LocationName,
CustomerNames = cus
};
var result = QCustInfo.SingleOrDefault();
if (result != null)
{
result.Addresses = db.Addresses.Where(a => a.CustomerId == result.CustomerNames.CustomerId);
};
return result;
}
public int CustomerId { get; set; }
[Display(Name = "Location Name")]
public string Location { get; set; }
public bool AllDay { get; set; }
[Display(Name ="Title")]
public string Title { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[DataType(DataType.DateTime), Required]
[DisplayFormat(DataFormatString = "{0:dd/mm/yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
[DataType(DataType.DateTime), Required]
[DisplayFormat(DataFormatString = "{0:dd/mm/yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "End Date")]
public DateTime EndDate { get; set; }
[Display(Name = "Customer Name")]
public string CustomerName { get; set; }
public virtual CustomerNames CustomerNames { get; set; }
public virtual IEnumerable<Addresses> Addresses { get; set; }
}

谢谢你的帮助!

您所说的"在不将事件从其自己的实体转移到CustomerEntity的情况下,有没有办法绕过这一问题?"的确切含义是什么?根据您所展示的内容,看起来您几乎是在为每个实体或每个顶级实体创建一个DbContext,如果这些实体是同一数据库的一部分,那么它真的会发出臭味。DbContext不是一个实体,它是许多实体的容器。对于较小的系统,它可以轻松地容纳所有实体,或者至少容纳所有需要直接查询的顶级实体。"实体"是表示数据库中表中一行的类。

通常情况下,我希望看到,如果您使用的是日历和客户,其中日历条目引用了一个客户和一组事件,那么我会有一个类似于以下内容的CalendarEntry实体:

public class CalendarEntry
{
public int CalendarEntryId { get; private set; }
// .. Other properties...
public virtual Customer Customer { get; private set; }
public virtual List<CalendarEvent> Events { get; private set; } = new List<CalendarEvent>();
}

具有类似的客户和事件实体定义。

可能只有一个DbContext:

public DbSet<CalendarEntry> CalendarEntries { get; }
public DbSet<Customer> Customers { get; }

另一方面,视图模型是简单的POCO类,其数据从实体复制,并且不包含实体。控制器控制DbContext的范围以读取实体,然后选择或将实体数据映射到视图模型中以传递到视图。它从视图接收视图模型,加载实体,并根据视图模型中的数据修改实体。

因此,对于接收要更新的日历事件的操作,我的目标是使用一个视图模型来表示新日历事件的方法,包括日历条目的ID。条目不会在日历事件的索引上创建,而是在此之前,索引只显示新条目的视图模型。因此,也许在日历控制器上会有一个创建新事件的操作,该操作将传递CalendarEntryID,在条目上创建CalendarEvent视图模型,然后重定向到日历事件视图。ViewModel是一个POCO,而不是一个实体,还没有向数据库提交任何内容。在CalendarEvent视图中,会有一个Save按钮,它接受该视图模型并控制实体的创建时间。。。

public ActionResult Save(CalendarEventViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
var calendarEntry = db.CalendarEntries.Include(x => x.Events)
.Single(x => x.CalendarEntryId = viewModel.CalendarEntryId);
var calendarEvent = new CalendarEvent
{
CalendarEntry = calendarEntry,
EventName = viewModel.EventName,
// .. etc.
};
calendarEntry.Events.Add(calendarEvent);
db.SaveChanges(); 
return RedirectToAction("Index");
}

这里的区别在于,视图模型完全独立于填充它们的实体。它们表示视图所需的数据,以及控制器定位适用实体所需的内容,仅此而已。您可以利用Automapper等工具来帮助在实体和视图模型之间高效地复制详细信息。请注意,在此示例中,DbContext(db(没有引用CalendarEvents的DbSet?事件是CalendarEntry的子项,上下文不需要每个实体都有DbSet,只需要我们关心直接查询的实体。(顶级实体(我不希望每个实体都有单独的DbContext,因为您无法利用实体之间的关系。在这种情况下,按CalendarEvent视图模型将有一个CalendarEntry ID,我可以使用它来定位条目。然后,我从视图模型的其余详细信息中填充事件的新实体,然后将该新条目添加到事件中,并调用SaveChanges。EF正在跟踪针对我加载的CalendarEntry所做的更改,它扩展到事件集合。我不需要跟踪单独的事件数据库集来添加到上下文中。

其他需要考虑的是DbContext(db(的范围。您希望确保Context被注入到您的控制器中,并且它的生存期范围仅限于请求的生存期。(使用IoC容器,如Autofac、Unity等(您需要非常确定它不是静态的或比请求活得更长,因为这将导致资源/性能问题,以及并发请求的潜在错误/异常。

最糟糕的情况是,如果您不确定DbContext的作用域是如何确定的,或者将其设置为静态,则应该将其用途更改为方法内部的作用域,然后读取依赖注入和IoC容器,将其作用域设置为请求,并用构造函数注入。

例如:

public ActionResult Save(CalendarEventViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
using( var db = new myDbContext())
{
var calendarEntry = db.CalendarEntries.Include(x => x.Events)
.Single(x => x.CalendarEntryId = viewModel.CalendarEntryId);
var calendarEvent = new CalendarEvent
{
CalendarEntry = calendarEntry,
EventName = viewModel.EventName,
// .. etc.
};
calendarEntry.Events.Add(calendarEvent);
db.SaveChanges(); 
}
return RedirectToAction("Index");
}

最新更新