Sascha
Sascha

Reputation: 189

MVC C# Metadata DataAnnotations confusion

I'm trying to use metadata classes to be able to keep our validation attributes when regenerating the model class. However, of the suggested recommendations I keep running into issues.

When updating or regenerating the model class, it creates/updates the DatabaseModel.edmx > DatabaseModel.tt with the auto-generated tablename.cs classes. These I can't edit if I don't want to lose the validation attributes.

As per Microsoft documentation, I created the Metadata.cs and Partial.cs classes in the Model folder or even tried creating the class per database table using the following sample code:

using System;
using System.ComponentModel.DataAnnotations;

namespace CApp.Models
{

    [MetadataType(typeof(ConsultantMetaData))]
    public partial class Consultant
    {
    }
    public class ConsultantMetaData
    {
        [Required]
        [Display(Name = "Name")]
        public string ConsultantName { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string ConsultantEmail { get; set; }

        [Required]
        [Display(Name = "Is Active")]
        public bool IsActive { get; set; }
    }
}

And this is when I start getting the ambiguity errors.

'Consultant' is an ambiguous reference between 'CApp.Data.Consultant' and 'CApp.Models.Consultant'

The default scaffolding creating the default Index, Details, Create, Edit and Delete controller and subsequent Views is the one affected. By default these all use the auto-generated model classes code to obtain and set data.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include ="ConsultantId,ConsultantName,ConsultantEmail,IsActive")] Consultant consultant)
{
    if (ModelState.IsValid)
    {
        db.Consultants.Add(consultant);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(consultant);
}

Or:

// GET: Consultants/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Consultant consultant = db.Consultants.Find(id);
    if (consultant == null)
    {
        return HttpNotFound();
    }
    return View(consultant);
}

I'll get the implicit conversion error:

Cannot implicitly convert type 'CApp.Data.Consultant' to 'CApp.Models.Consultant'

In the method, if I change the Consultant to:

CApp.Data.Consultant consultant = db.Consultants.Find(id);

Then the data is obtained as per usual and the Display validation attribute is ignored and still displays the table column name instead of the wanted Display Name attribute.

Not sure how I should retrieve and display the model data so that it displays the information I need besides manually typing out the needed column name in the view which is not the objective. I feel like I'm running around in circles here and I haven't found the answer I need yet in this forum.

Am I also correct in assuming that the metadata class, containing the wanted data annotations, needs to match the auto-generated model exactly in terms of columns in the table or do I only need to specify the custom columns for which I need the custom Display Name or other custom attributes?

Any help will be appreciated. Desperately need explanation with examples please.

Upvotes: 1

Views: 366

Answers (1)

roozbeh S
roozbeh S

Reputation: 1124

Seems you have a Consultant class under CApp.Data namesmapce while you are trying to add the metadata in CApp.Models namespace!

you should consider using same namespaces. You need to change CApp.Models to CApp.Data (or the other way but I don't recommend).

Your original definition for Consultant should be:

namespace CApp.Data
{
    public partial class Consultant
    {
        // some codes
    }
}

Pay attention that the original definition of the class must be partial also. Then your metadata should be in the same namespace:

using System;
using System.ComponentModel.DataAnnotations;

namespace CApp.Data
{

    [MetadataType(typeof(ConsultantMetaData))]
    public partial class Consultant
    {
    }
    public class ConsultantMetaData
    {
        [Required]
        [Display(Name = "Name")]
        public string ConsultantName { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string ConsultantEmail { get; set; }

        [Required]
        [Display(Name = "Is Active")]
        public bool IsActive { get; set; }
    }
}

Upvotes: 1

Related Questions