在 ASP.NET MVC 中保存具有外键的实体
我需要一些帮助来做一些我认为很简单的事情。我正在使用 ASP.net MVC 3 和 CodeFirst (CTP5)
我有两个实体:公司和位置。一家公司可以有多个地点。类如下(删除所有不必要的信息)
public class Company
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
public class Location
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public virtual Company Company { get; set; }
}
现在在我的控制器中,我只允许在公司上下文中创建位置,因此始终传入公司 ID(在视图中,我显示公司名称)在只读字段中,但不允许用户更改/编辑它。
public ActionResult Create(int companyId)
{
Company company = _session.Single<Company>(c => c.Id == companyId);
Location newLocation = new Location {Company = company};
return View(newLocation);
}
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
_session.Add<Location>(location);
_session.CommitChanges();
return RedirectToAction("Index");
} else {
return View(location);
}
}
现在,每当我尝试创建新位置时,ModelState.IsValid 始终为 false,因为未提供并且已提供 location.Company.Name。 Company 上的必填字段。我从来没有尝试在这里创建一家新公司,我只是尝试创建一个引用正确公司的位置,我不想将 Name 属性添加到视图中只是为了获取 ModelState。我应该如何轻松地实现这一点?
I need some help doing something I am assuming is simple. I am using ASP.net MVC 3 with CodeFirst (CTP5)
I have two entities: Company and Location. A Company can many Locations. The classes are as follows (stripped of all needless info)
public class Company
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
public class Location
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public virtual Company Company { get; set; }
}
Now in my controller, I only allow creating a Location within the context of a Company, so the Company Id is always passed in (In the View, I show the name of the Company in a read only field, but do not allow the user to change/edit that.
public ActionResult Create(int companyId)
{
Company company = _session.Single<Company>(c => c.Id == companyId);
Location newLocation = new Location {Company = company};
return View(newLocation);
}
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
_session.Add<Location>(location);
_session.CommitChanges();
return RedirectToAction("Index");
} else {
return View(location);
}
}
Now whenever I try to create a new location, the ModelState.IsValid is always false due to the fact that location.Company.Name is not supplied and is a required field on Company. I am never trying to create a new company here, I merely trying to create a location with a reference to the correct company. I don't want to add the Name property to the view just to get the ModelState to validate. How can this be accomplished easily? Should I be passing something different from the view? or to the view?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您只需在
表单
上添加包含公司名称的隐藏字段即可轻松完成。它的数据将被拾取并按预期发送到您的 POST 操作。如果您根本不在服务器端使用公司名称,则只要公司 ID 正确(因为您使用它),您就可以使用任何虚拟文本填充此隐藏字段。但如果您确实需要正确的公司名称和 ID,则必须将实际名称填写到隐藏字段中,而不是一些虚拟文本。
反正。这将为您的公司名称添加一个隐藏字段
,如果您没有将 Company 属性设置为任何内容,您也可以使用此值:
无论该值是实际名称还是虚拟名称,都并不重要。您的模型将在回发后生效。
定期回发还是 Ajax?
我想您使用的是常规回发而不是 Ajax 回发。如果您确实使用 Ajax,并且使用 jQuery 并调用
$.post()
或$.ajax()
或类似的东西,那么您始终可以提供您想要的任何对象。喜欢。您可以在 JavaScript 代码中填写公司名称的任意值。如果您在 JavaScript 中有复杂的对象,并且希望使用强类型参数将它们发送到控制器操作(以便对它们进行验证),您可以使用 我的这个小 jQuery 插件,它准备将任何 JSON 对象正确发送到 Asp.net MVC 操作,以便数据将被自动将数据绑定到您的强类型以进行验证。 JavaScript 也有日期功能。 :)
其他信息
由于您(在评论中)指出您的实体正在进行大量工作,并且您可能会添加/删除必填字段,因此更改所有相关视图可能会非常乏味。
然而,当涉及到可更改的模型和不同的验证场景(取决于对象状态)时,您可以采取一些方法,让您的生活变得更轻松:
为创建过程提供单独的视图模型,其中还包括自动转换为应用程序的功能模型类:
使用继承:
You can easily just add a hidden field with company name on your
form
. Its data will get picked up and sent to your POST action as expected.If you don't use company name on the server side at all, you can fill this hidden field up with any dummy text as long as company ID is correct (because you use it). But if you do need correct company name with your ID, you'll have to fill actual name into hidden field instead of some dummy text.
Anyway. This will add a hidden field for your company name
and if you don't have your Company property set to anything then, you can as well use this:
Whether this value is an actual name or a dummy name it doesn't really matter. Your model will be valid upon postback.
Regular postback or Ajax?
I suppose you're using a regular postback and not an Ajax postback. If you do use Ajax one and you're using jQuery and calling
$.post()
or$.ajax()
or something similar, you can always provide whatever object you like. You can fill any value for the company name in your JavaScript code.If you have complex objects in JavaScript and would like to send them to your controller action with strong type parameters (so they will be validated) you can use this little jQuery plugin of mine, that prepares any JSON object to be correctly sent to Asp.net MVC action so data will be automatically data bound to your strong types to get validated. Does JavaScript dates as well. :)
Additional information
Since you pointed out (in a comment) that your entities are very much work in progress and you may be adding/removing required fields, this may be very tedious to also change all related views.
Yet there are ways you may take, to make your life a bit easier when it comes to changable models and different validation scenarios depending on object state:
Have separate view models for creation process that also include functionality to auto-convert to application model class:
Use inheritance:
正如我在评论中解释的那样,我偶然发现了一个使这项工作有效的小变化,仍然认为这是一个拼凑,因为我不明白为什么我需要复制子 ID 参数,但我能找到的最好/最简单的一个应该是将来很容易更新,我将是第一个承认我不知道 CodeFirst 中的全部(或大部分)内容的人。
我将位置类从: 更改
为:
现在据我所知,这根本没有改变数据库架构,但现在位置(验证时)唯一需要的是 CompanyId 中有一个 ID字段中,Company 属性可以保留为 null(之前,它尝试验证正确的 Company,因为需要 Company 类)。
因此,现在在创建位置时,视图唯一需要在 CompanyId 中传回的内容。
有人看到任何缺点吗?或者更好的方法?
As i was explaining in my comment, i stumbled upon a small change which makes this work, still think it is a kludge as I dont see why i need to duplicate the child Id paramater, but the best/simplest one I could find that should be easy to update in the future and I will be the first to admit I dont know all (or most) of what there is in CodeFirst.
I changed the location class from:
To:
Now as far as I can tell, this hasnt changed the database schema at all, but now the only thing which is required on the Location (when validating) is that there is an ID in the CompanyId field, the Company property can be left null (before, it was trying to validate a proper Company since the Company class was required).
So now when creating a Location, the only thing the view needs to pass back in the CompanyId.
Anyone see any drawbacks? or a better way?