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?
[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....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With