1

I was wondering how to generate a Controller for a derived Model. Let me give you a big picture. There is a Man class from which the User class is inherited. As I read in several EF tutorials, I didn't define DbSet in DatabaseContext class. Thus, as you may know there would be generated only one table on the SQL containing all the properties of Man and User classes besides a discriminator column.

The problem appeared as I was trying to figure out a Controller class for User, but as it is not mentioned in the DatabaseContext, MVC cannot create it and ends up with an error. Hence, I created a Controller for Man class, the parent class. I also have done some modifications in the Controller to explicitly point to the User. I mean, using OfType<> and as such. But as I came to the View which is created based on MVC scaffolding, I could not find any User properties, those that are dedicated to this class.

The Following is the Man class:

public class Man
{
    [Key]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public long ID { get; set; }
    //------------------------------------------------------------//
    [Required, MaxLength(20)]
    [LocalizedAttribute("FName")]
    public string FName { get; set; }
    //------------------------------------------------------------//
    [Required, MaxLength(20)]
    [LocalizedAttribute("LastName")]
    public string LastName { get; set; }
    //------------------------------------------------------------//
    [Required]
    [RegularExpression("^[0-9]+$", ErrorMessageResourceName = "ErrorOnlyNumbers", ErrorMessageResourceType = typeof(MAHAL_E_MA_Model.Properties.Resources))]
    [LocalizedAttribute("Mobile")]
    public string Mobile { get; set; }
    //------------------------------------------------------------//
    [LocalizedAttribute("Phone")]
    [RegularExpression("^[0-9]+$", ErrorMessageResourceName = "ErrorOnlyNumbers", ErrorMessageResourceType = typeof(MAHAL_E_MA_Model.Properties.Resources))]
    public string HomePhone { get; set; }
    //------------------------------------------------------------//
    [RegularExpression("^[0-9]+$")]
    [LocalizedAttribute("IDCardNumber")]
    public string IDCardNumber { get; set; }
    //------------------------------------------------------------//
    [RegularExpression("^[0-9]+$")]
    [LocalizedAttribute("NationalCode")]
    public string NationalCode { get; set; }
    //------------------------------------------------------------//
    [MaxLength(10)]
    [LocalizedAttribute("DOB")]
    public int DOB { get; set; }
    //------------------------------------------------------------//
    [Required]
    public int CityID { get; set; }
    [ForeignKey("CityID")]
    public virtual City CityParent { get; set; }
    //------------------------------------------------------------//
    [MaxLength(100)]
    [LocalizedAttribute("Address")]
    public string Address { get; set; }
    //------------------------------------------------------------//
    [LocalizedAttribute("PostalCode")]
    public string PostalCode { get; set; }
    //------------------------------------------------------------//
    [MaxLength(255)]
    [LocalizedAttribute("PhotoPath")]
    public string PhotoPath { get; set; }
}

Here the User Class:

public class User : Man
{
    [MaxLength(20)]
    [LocalizedAttribute("Username")]
    public string UserName { get; set; }
    //------------------------------------------------------------//
    [DataType(DataType.Password)]
    [MaxLength(100), MinLength(6, ErrorMessageResourceType = typeof(MAHAL_E_MA_Model.Properties.Resources), ErrorMessageResourceName = "ErrorPasswordLength")]
    [LocalizedAttribute("Password")]
    public string Password { get; set; }
    //------------------------------------------------------------//
    [DataType(DataType.Password)]
    [Compare("Password", ErrorMessageResourceType = typeof(MAHAL_E_MA_Model.Properties.Resources), ErrorMessageResourceName = "ErrorConfirmPassword")]
    [LocalizedAttribute("ConfirmPassword")]
    public string ConfirmPassword { get; set; }
    //------------------------------------------------------------//
    [DataType(DataType.EmailAddress, ErrorMessageResourceType = typeof(MAHAL_E_MA_Model.Properties.Resources), ErrorMessageResourceName = "ErrorEmailInvalid")]
    [MaxLength(20)]
    [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")]
    [LocalizedAttribute("Email")]
    public string Email { get; set; }
    //------------------------------------------------------------//
    [MaxLength(30)]
    [LocalizedAttribute("Title")]
    public string Title { get; set; }
    //------------------------------------------------------------//
    [MaxLength(10)]
    [LocalizedAttribute("HireDate")]
    public int HireDate { get; set; }
    //------------------------------------------------------------//
    [LocalizedAttribute("ReportsTo")]
    public long ReportsTo { get; set; }
    [ForeignKey("ReportsTo")]
    public virtual IList<User> ReportsChild { get; set; }
}

Additionally, I did use OfType in my controller and view as such:

//
    // GET: /User/

    public ViewResult Index()
    {
        //var mans = db.Mans.Include(m => m.CityParent);
        return View(unitOfWork.UserRepository.Get(orderBy: us => us.OrderByDescending(u => u.ID)).OfType<User>().ToList());
    }

 // POST: /User/Create

    [HttpPost]
    public ActionResult Create(Man man)
    {
        if (ModelState.IsValid)
        {
            //db.Mans.Add(man);
            //db.SaveChanges();
            MAHAL_E_MA_Model.POCO.User user = (MAHAL_E_MA_Model.POCO.User)man;
            unitOfWork.UserRepository.InsertData(user);
            unitOfWork.UserRepository.Save();
            return RedirectToAction("Index");  
        }

       // ViewBag.CityID = new SelectList(db.Cities, "CityID", "Name", man.CityID);
        return View(man);
    }

And in the view:

 @foreach (var item in Model)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.FName.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.LastName.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>

        <td>
            @Html.DisplayFor(modelItem => item.Mobile.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.HomePhone.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.IDCardNumber.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.NationalCode.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.DOB)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CityParent.Name.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Address.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.PostalCode.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.PhotoPath.OfType<MAHAL_E_MA_Model.POCO.User>())
        </td>
        <td>
            @Html.ActionLink(GeneralResource.Edit, "Edit", new { id = item.ID }) |
            @Html.ActionLink(GeneralResource.ViewDetails, "Details", new { id = item.ID }) |
            @Html.ActionLink(GeneralResource.Delete, "Delete", new { id = item.ID })
        </td>
    </tr>

By the way, I never mentioned the DbSet<User> whereas mentioned DbSet<Man>. The reason; EF will create ONE table with all the Properties of User and Man called Man table. Distingushing between different inherited classes such as Customer (not mentioned in this question) and the User is possible using the Discriminator column.

Sorry if the story gets long, But how Can I handle it? That is, how may I have the User Properties in the View? Do I have to add them on myself but if so I don't have any access to them as spreading out some DisplayFor() contained in the foreeach loop which creates the data entry form.

Hope I could transfer the problem successfully.. Can anyone help me on this? Regards,

Ali
  • 847
  • 2
  • 13
  • 37
  • 1
    Please show the code of your classes and the code where you use `OfType`. (And please use paragraphs). – Gert Arnold May 26 '13 at 20:58
  • Why you "didn't define `DbSet`" in your `context`? – haim770 May 26 '13 at 21:03
  • @GertArnold and @haim770; Sorry for the delay actually as I pose the question it was night late... Now making some paragraphs, bringing the code, and also the reason why not mentioning the DbSet.. Hope you may help me on this – Ali May 27 '13 at 04:07
  • you use entity framework and you drag your all tables in .edmx file is it right. – Rajpurohit May 27 '13 at 05:09
  • No, it's not. I use EF Code First. – Ali May 27 '13 at 05:49

1 Answers1

0

It looks like you're trying to do a table per hierarchy mapping. You might need to define the mapping to Man based on the discriminator values:

   public class MyDbContext: DbContext
   {
     // Constructor here


     public DbSet<Man> Man {get;set;} 

     protected override void OnModelCreating(DbModelBuilder modelBuilder)
     {
         modelBuilder.Entity<Man>().Map<User>(c => c.Requires("UserTypeId").HasValue(1));

     }

}

Then your repository can query the context for User and return a User object to the view. Change the view to @model IList<User> and you should be able to use User with the Man properties. You can map additional types such as modelBuilder.Entity<Man>().Map<Customer>(c => c.Requires("UserTypeId").HasValue(2)

I'm assuming there is you're using EF 5 and there is a discriminator column called UserTypeId.

Hope that helps!

JaySilk84
  • 960
  • 7
  • 19
  • Thanks you, You just got the point, but is this a must to define DbSet in the DatabaseCOntext? as I studied a tutorial, there is no need to do that.. – Ali May 27 '13 at 05:52
  • I've done as you suggested but now facing a new error: "The type MAHAL_E_MA_Model.POCO.Man cannot used as type parameter 'TDerived' in the generic type or method... There is no implicit reference conversion from MAHAL_E_MA_Model.POCO.Man to MAHAL_E_MA_Model.POCO.User" Do you have any idea? – Ali May 27 '13 at 06:02
  • Sorry, I had the inheritance backwards. I corrected the code sample. Your controller should be using the User repository to query for a User object from the Man table. – JaySilk84 May 27 '13 at 06:29
  • @JaySkill84: I already know this.. But you know the problem is on the View file!! I have no access to the User Class properties derived from the Man there.. Even when consider the corrections you've made. So any idea now? – Ali May 27 '13 at 07:14
  • Did you specify the model at the top of the view? The top of the view should have `@model IList` I made an edit to the answer where I had the wrong type and casing – JaySilk84 May 27 '13 at 07:26
  • I hope I'm not bothering you with this error handling and I really appreciate your generosity, But... there was `@model Man` at the top of the View that I've just changes into `@model IList` that by this way, a vice-verse situation happens, that is, no properties of the Man class is available!! – Ali May 27 '13 at 07:58
  • Actually, I went through this guide but failed to resolve the issue:[link](http://stackoverflow.com/questions/9417888/mvc-3-model-binding-a-sub-type-abstract-class-or-interface) – Ali May 27 '13 at 07:59