实体框架中的多态 [英] Polymorphism in Entity Framework

查看:161
本文介绍了实体框架中的多态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在控制器上不具体的类( BankAccount CreditCard )。



我遇到这个问题。



我正在使用此网站的示例:



< -2-table-per-type-tpt.aspxrel =nofollow noreferrer> 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



视图 / p>

CreateUser



如果 CreditCard 被选中,它应该与用户类相关联。





图表





钍e code



UserController

  [HttpPost] 
public ActionResult Create(User user)//上面的Watch来自这个用户实例
{
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



<$ p $ TPMVC.Models

< script src =http://ajax.microsoft.com/ajax/jQuery/jquery-1.5.jstype =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 =创建;
}

< h2>创建< / 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 =submitvalue =Create/>
< / p>
< / fieldset>
}

< div>
@ Html.ActionLink(返回列表,索引)
< / div>

类代码:

 命名空间TPTMVC.Models {
public class BillingDetail
{
[Key]
[ForeignKey(user)]
public int UserID {得到;组; }
public string Owner {get;组; }
public string Number {get;组; }
public virtual User user {get;组; }
}}

命名空间TPTMVC.Models {
public class User
{
public int UserId {get;组; }
public string FirstName {get;组; }
public string LastName {get;组; }

public virtual BillingDetail billingDetail {get;组; }
}}
命名空间TPTMVC.Models {
[Table(BankAccounts)]
public class BankAccount:BillingDetail
{
public string BankName {得到;组; }
public string Swift {get;组;
}}
命名空间TPTMVC.Models {
[表(CreditCards)]
public class CreditCard:BillingDetail
{
public int CardType {得到;组; }
public string ExpiryMonth {get;组; }
public string ExpiryYear {get;组; }
}}

问题



当我点击创建按钮时,我得到这个结果:





我选择了一个 CreditCard ,但结果是 BillingDetail 。我试图做一个投射,但我有一个错误,你可以看到。



:(



为什么只有 BillingDetail 出现在 UserController



我的第一个解决方案



  [HttpPost] 
public ActionResult创建(用户用户,信用卡卡,BankAccount银行,字符串收音机)
{
// String teste = formCollection [Radio];
if(ModelState.IsValid)
{
switch(Radio)
{
caseCredCard:
user.billingDetail = card;
break;
caseBank:
user.billingDetail = bank;
break;
}
context.User.Add(user);
context.SaveChanges();
return RedirectToAction(Index);
}

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


解决方案

c $ c> User 对象到您的View。这有一个导航属性到 BillingDetail 可以是一个 CreditCard 或一个 BankAccount 。您可以在View (CreditCard)模型(BankAccount)模型中投射。当你创建一个实例是空的时,它会工作,但是当你有一个非空的实例时会导致运行时错误,因为其中一个转换将失败。



要修复,您可以使用$ code>模型作为CreditCard 和模型作为BankAccount ,然后检查它们不是在渲染适当的编辑器之前为null。但是,当您的用户想要更改付款方式时,您需要确定该做什么。



当表单返回给控制器时,因为您的创建方法签名采用用户参数,默认的 ModelBinder 知道它应该实例化一个用户。这是非常有能力的,但是它无法解决与 FormCollection 中出现的与$ code> BillingDetail相关的值。



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



了解ModelBinding



自定义模型绑定器 - 一个人的意见



我去的解决方案 - 但看看这里的所有其他解决方案!



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

  public ActionResult CreateOrEdit(FormCollection values)
{
// FormCollection是属性或块
BaseProperty模型;
if(values [PropertyTypeID]!= null)
{
//它必须是属性!
Property property = new Property();
TryUpdateModel(property);
_Uow.PropertyRepository.InsertOrUpdate(property);
model = property;
}
else
{
块块=新块();
TryUpdateModel(block);
_Uow.BlockRepository.InsertOrUpdate(block);
model = block;
}
// etc ....


The concrete classes (BankAccount and CreditCard) are not visible on controller.

I'm stuck with this issue.

I'm using the example from this site:

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

The view

The CreateUser:

If the CreditCard was selected it should be associated to the User class.

The diagram

The code

UserController:

    [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>

Classes code:

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; }
}}

The problem

When I click the create button, I get this result:

I selected a CreditCard but the result was BillingDetail. I tried to make a casting but I got a error, as you can see.

:(

Why only BillingDetail appear on UserController?

My first solution

[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);
        }

解决方案

You are passing a User object to your View. This has a navigation property to BillingDetail which can be a CreditCard or a BankAccount. You cast it like this in the View (CreditCard)model and (BankAccount)model. It will work when your creating because you are casting an instance that is null, but it will cause a run-time error when you have a non-null instance because one of the casts will fail.

To fix that you can use model as CreditCard and model as BankAccount then check they are not null before you render the appropriate editors. But you'll need to work out what to do when your user wants to change the payment method.

When the form is returned to the controller, because your Create method signature takes a User parameter, the default ModelBinder knows that it should instantiate a User. It is quite capable of that, but it is not able to work out what to do with the values that appear in the FormCollection that relate to the BillingDetail.

With inheritance you can't rely on the default ModelBinder. You need to work out what suits you best. Here's some references I found useful:

Get an understanding of ModelBinding

Custom model binders - one person's opinion

The solution I went with - but look at all the other solutions here too!

Here's some example code from my project that should give you an idea:

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....

这篇关于实体框架中的多态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆