3

具体类 (BankAccountCreditCard) 在控制器上不可见。

我被这个问题困住了。

我正在使用此站点中的示例:

http://weblogs.asp.net/manavi/archive/2010/12/28/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt。 aspx

风景

CreateUser: _

如果CreditCard选择了它,它应该与User类相关联。

创建用户

图表

类图

编码

用户控制器

    [HttpPost]
    public ActionResult Create(User user)//The Watch above came from this user instance
    {
        if (ModelState.IsValid)
        {

            context.User.Add(user);
            context.SaveChanges();
            return RedirectToAction("Index");  
        }

        ViewBag.PossibleBillingDetail = context.BillingDetail;
        return View(user);
    }

User\_CreateOrEdit.cshtml

在此处输入图像描述

User\Create.cshtml

    @model TPTMVC.Models.User
    @using TPTMVC.Models;

<script src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.5.js" type="text/javascript"></script> 
<script type="text/javascript">

    $(document).ready(function () {
        $('.divbank').hide();
        $('input[type=radio]').live('change', function () { updateweather(); });
    });

    function updateweather() {
        //alert();
        if ($('input[type=radio]:checked').val() == 'Bank') {
            $('.divcard').fadeOut(1000);
            $('.divcard').hide();
            $('.divbank').fadeIn(1000);
        }
        else {
            $('.divbank').fadeOut(1000);
            $('.divbank').hide();
            $('.divcard').fadeIn(1000);
            }

    }
        </script>
    <div id="json"></div>

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
    <legend>User</legend>

        @Html.Partial("_CreateOrEdit", Model)

        <div ='none' class="divcard">
            <div class="editor-label">
                @Html.LabelFor(model => ((CreditCard)model.billingDetail).ExpiryMonth)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => ((CreditCard)model.billingDetail).ExpiryMonth)
                @Html.ValidationMessageFor(model => ((CreditCard)model.billingDetail).ExpiryMonth)
            </div>

             <div class="editor-label">
                @Html.LabelFor(model => ((CreditCard)model.billingDetail).ExpiryYear)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => ((CreditCard)model.billingDetail).ExpiryYear)
                @Html.ValidationMessageFor(model => ((CreditCard)model.billingDetail).ExpiryYear)
            </div> 
        </div>

        <div='none' class="divbank">
            <div class="editor-label">
                @Html.LabelFor(model => ((BankAccount)model.billingDetail).BankName)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => ((BankAccount)model.billingDetail).BankName)
                @Html.ValidationMessageFor(model => ((BankAccount)model.billingDetail).BankName)
            </div>

             <div class="editor-label">
                @Html.LabelFor(model => ((BankAccount)model.billingDetail).Swift)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => ((BankAccount)model.billingDetail).Swift)
                @Html.ValidationMessageFor(model => ((BankAccount)model.billingDetail).Swift)
            </div> 
        </div>  
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

类代码:

namespace TPTMVC.Models{
public class BillingDetail
{
    [Key]
    [ForeignKey("user")]
    public int UserID { get; set; }
    public string Owner { get; set; }
    public string Number { get; set; }
    public virtual User user { get; set; }
}}

namespace TPTMVC.Models{
public class User
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual BillingDetail billingDetail { get; set; }
}}
namespace TPTMVC.Models{
    [Table("BankAccounts")]
    public class BankAccount:BillingDetail
    {
        public string BankName { get; set; }
        public string Swift { get; set; }
    }}
namespace TPTMVC.Models{
[Table("CreditCards")]
public class CreditCard:BillingDetail
{
    public int CardType { get; set; }
    public string ExpiryMonth { get; set; }
    public string ExpiryYear { get; set; }
}}

问题

当我单击创建按钮时,我得到以下结果:

手表

我选择了一个CreditCard,但结果是BillingDetail。如您所见,我尝试进行铸造,但出现错误。

:(

为什么只BillingDetail出现在UserController

我的第一个解决方案

[HttpPost]
        public ActionResult Create(User user, CreditCard card, BankAccount bank, String Radio)
        {
            //String teste=formCollection["Radio"];
            if (ModelState.IsValid)
            {
                switch (Radio)
                {
                    case "CredCard":
                        user.billingDetail = card;
                        break;
                    case "Bank":
                        user.billingDetail = bank;
                        break;
                }
                context.User.Add(user);
                context.SaveChanges();
                return RedirectToAction("Index");  
            }

            ViewBag.PossibleBillingDetail = context.BillingDetail;
            return View(user);
        }
4

3 回答 3

2

您正在将一个User对象传递给您的视图。这有一个导航属性BillingDetail,可以是 aCreditCard或 a BankAccount。你在 View(CreditCard)model(BankAccount)model. 它会在您创建时起作用,因为您正在转换一个为空的实例,但是当您有一个非空实例时它会导致运行时错误,因为其中一个转换将失败。

要解决这个问题,您可以使用model as CreditCardmodel as BankAccount然后在呈现适当的编辑器之前检查它们不为空。但是,当您的用户想要更改付款方式时,您需要弄清楚该怎么做。

当表单返回给控制器时,因为您的Create方法签名需要一个User参数,所以默认ModelBinder知道它应该实例化一个User. 它非常有能力,但它无法弄清楚如何处理出现在FormCollectionBillingDetail.

使用继承,您不能依赖默认值ModelBinder。你需要找出最适合你的东西。以下是我发现有用的一些参考资料:

了解 ModelBinding

自定义模型活页夹 - 一个人的意见

我采用的解决方案- 但也请查看此处的所有其他解决方案!

这是我的项目中的一些示例代码,应该会给您一个想法:

public ActionResult CreateOrEdit(FormCollection values)
{
    //The FormCollection is either a Property or a Block
    BaseProperty model;
    if (values["PropertyTypeID"] != null)
    {
        //it must be a Property!
        Property property = new Property();
        TryUpdateModel(property);
        _Uow.PropertyRepository.InsertOrUpdate(property);
        model = property;
    }
    else
    {
        Block block = new Block();
        TryUpdateModel(block);
        _Uow.BlockRepository.InsertOrUpdate(block);
        model = block;
    }
    //etc....
于 2013-10-18T13:31:38.297 回答
1

我认为这里有一些问题:

  1. 你没有坚持关注点分离。如果您提供的模型图适用于您的实体,则不应将它们用作前端模型。您的数据层和视图层应该有单独的模型——这让您可以将数据的设计方式与用户交互的方式分离。
  2. 如果我错了,所以用户请纠正我,但你不能在网页数据中返回具体的服务器端对象。在这种情况下,您尝试将BillingDetailac# 类转换为您的视图的模型,然后将其与表单提交一起返回。据我所知,您只能在表单提交中返回纯数据和表单字段。可以让您的视图模型包含其他视图模型和具体类,但您只能返回普通字段和其中包含普通字段的视图模型。
  3. 您正在尝试将基类转换为派生类。当您将派生类作为基类传递,然后在其他地方重新转换它时,这是可能的,但是您不能采用纯基类并将其转换为更具体的对象。这就像试图将矩形强制为正方形。

明智的解决方案你应该这样做:

  • CreditCard为和创建 2 个单独的视图模型BankAccount,每个模型都有各自的属性。(您应该为您的User对象做同样的事情,以便遵守 SoC)
  • 使用具有两个新视图模型的模型填充您的视图,而不是BillingDetail.
  • 提交表单时,使用单选按钮作为控制器中的条件来确定用户选择的付款类型,然后创建相应的对象,将视图模型属性映射到具体对象,将其添加到用户,然后保存.
于 2013-10-17T20:23:16.773 回答
0

尽管我同意马特的观点,但使用视图模型通常是一个好主意,您的问题的直接原因在于

ViewBag.PossibleBillingDetail = context.BillingDetail;

这也包括BankAccounts,因此某些BillingDetail对象不能强制转换为CreditCard

将行替换为

ViewBag.PossibleBillingDetail = context.BillingDetail.OfType<CreditCard>();
于 2013-10-18T08:58:59.210 回答