I have refactored an application which uses EF5 Database First. The application uses metadata classes to add data annotations to the entity properties. Before the refactor, these worked. They are mostly just display names and data formats.
Example:
[MetadataType(typeof(QueryDetailsResultMetadata))]
public partial class QueryDetailsResult : IPortfolio
{
public string Source { get { return "Local"; } }
}
public class QueryDetailsResultMetadata
{
//Fields from QueryDetailsResult requiring annotations
[Display(Name = "Company Name")]
public string SiteName { get; set; }
[Display(Name = "Contact Telephone Number")]
public string ContactTelNo { get; set; }
}
Before the refactor, the partial class did not inherit from an interface and it did not have the non mapped property. These changes are however required. Neither of these two should be causing a problem as both are well documented as valid solutions.
The interface looks like this
public interface IPortfolio
{
int Id { get; set; }
string SiteName { get; set; }
string YearOfManufacture { get; set; }
string Contact { get; set; }
string ContactTelNo { get; set; }
string Source { get;}
}
The display uses the properties like this
#Html.DisplayNameFor(model => model.Portfolio.ContactTelNo)
On the View at runtime, the property names are shown rather than the display names. Any ideas why? I can't see any reason for the annotations to be broken
//edit
I tried moving the annotations on to the new non-mapped fields in the partial and removed them from the metadata class. To seee if it had any effect. None. Also double checked the edmx is in the same Namespace as the partial class and metadata file which it is.
Any thoughts on what to check or try? Not having much success this end, most google results are just saying to use a metadata class which is already in place.
//2nd Edit
Moving annotations out of metadata class and on to the interface did the trick.
It is a little bit confusing how the DataAnnotation attributes are wroking with interfaces because:
at one hand classes don't inherit attributes from their interfaces: Is it possible to use DataAnnotations with Interfaces?
the the other hand you can/need put the attributes on the interfaces: ASP.NET MVC DisplayAttribute and interfaces
But how it works only depends on the type of your "container" in the view so lets consider the following types:
public class QueryDetailsResult : IPortfolio
{
public string SiteName { get; set; }
}
public interface IPortfolio
{
string SiteName { get; set; }
}
So if you have #Html.DisplayNameFor(model => model.Portfolio.SiteName) in your view
and your model class looks like
public class Model {
public QueryDetailsResult Portfolio { get; set; }
}
then you need to put the DisplayAttribute on the SiteName property of your QueryDetailsResult class because MVC looks for the "container" type in the model.Portfolio.SiteName expression which is QueryDetailsResult
but if you have your model class defined as
public class Model {
public IPortfolio Portfolio { get; set; }
}
then you need to put it on the SiteName property of the IPortfolio interface because your "container" type is the IPortfolio interface.
Related
I have set up my EF code-first database but want to add additional derived properties. (Yes, it should be in a view model, we can discuss another time why it is this way.) I have created a partial class extending the actual table class. If I add a [NotMapped] to the new partial, will it avoid mapping the additional properties I add there or will it apply to the entire class?
It will apply to the entire class. Remember that a partial class is simply a way of splitting a class into multiple files. From the official docs:
At compile time, attributes of partial-type definitions are merged.
So this:
[SomeAttribute]
partial class PartialEntity
{
public string Title { get; set; }
}
[AnotherAttribute]
partial class PartialEntity
{
public string Name { get; set; }
}
Is equivalent to writing:
[SomeAttribute]
[AnotherAttribute]
partial class PartialEntity
{
public string Title { get; set; }
public string Name { get; set; }
}
If you want to add a partial class without having the properties included in the model, you will need to add the NotMapped attribute to the individual items:
partial class PartialEntity
{
public string Title { get; set; }
}
partial class PartialEntity
{
[NotMapped]
public string Name { get; set; }
}
I'm using Entity Framework 5 in Database First approach and I am using edmx file.
Most of my entities have 6 common fields. Fields like CreatedAt, CreatedBy etc. Now, I implemented some functions as extensions that can only be applied to IQueryable of those Entities that have the common fields. But when I implement the extension method, it can be accessed by any type of IQueryable as it's typed T and I can only define that the type T should always be of one type.
So, I thought I can give a base class to the entities which has common fields and define type T as that base type. But, it seems I can't do this.
Any idea on how to solve this or implement what I have explained above?
Don't create a base class. Create an Interface, like below:
public interface IMyEntity
{
DateTime CreatedAt { get; set; }
string CreatedBy { get; set; }
// Other properties shared by your entities...
}
Then, your Models will be like this:
[MetadataType(typeof(MyModelMetadata))]
public partial class MyModel : IMyEntity
{
[Bind()]
public class MyModelMetadata
{
[Required]
public object MyProperty { get; set; }
[Required]
public string CreatedBy { get; set; }
}
}
I'm a fan of:
public interface IShared
{
DateTime CreatedOn { get; set; }
}
public interface ISharedValidation
{
[Required]
DateTime CreatedOn { get; set; }
}
public interface IMyEntity: IShared
{
// Entity Specifics
string Username { get; set; }
}
public interface IMyEntityValidation: ISharedValidation
{
[Required]
string Username { get; set; }
}
Then, your Models will be like this:
[MetadataType(typeof(IMyEntityValidation))]
public partial class MyModel : IMyEntity
{
public object CreatedOn { get; set; }
public string Username { get; set; }
}
If T4 generated by Entity Framework then your non-autogenerated class would look like:
[MetadataType(typeof(IMyEntityValidation))]
public partial class MyModel : IMyEntity
{
}
Typically, it is not recommended to use Bind in Asp.Net MVC.
I have a model and a partial model which contains only the properties that I need to expose in JSON.
But the properties between the model and his partial model are redundant.
How can I avoid that or improve my approach?
namespace Dashboard.Models.UserModels
{
public class UserModel
{
public int id { get; set; }
public string dbName { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public int idExternal { get; set; }
public int idInstance { get; set; }
public string login { get; set; }
public string password { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public LuccaUserModel()
{
idInstance = -1;
}
// partial model for json result
// not sure is the best way or have to be here
public class PartialUserModel
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string url { get; set; }
}
// UserModel Methods
}
}
You can rename PartialUserModel UserModelBase class (or leave it as is... it just makes better logical sense to do so) and make UserModel to inherit from it:
public class UserModel : UserModelBase
{
...
}
Of course you'll need to remove all duplicate properties from UserModel in this case.
It's a thin line between doing a proper design and building an overkill design. Answer depends on many inputs, among which I chose to have project and model breadth most important.
In hope to have my answer clearer, I have to say I use different terminology. Data which is adopted for use in UI is usually called ViewModel. In your case, you would build UserViewModel which contains necessary subset of information.
If I'm working on a one-off project, I'll reuse model as a ViewModel. I'll do this by having helper method which removes sensitive information, loads up or cuts off data which is lazy loaded from database and does other preparation on data. All this is done with same model class.
If it's not a short term project, I look to create separate ViewModel classes which I map from model data. Then, if I'm working with mostly flat data I use AutoMapper tool to have data automatically copied, instead of writing my own mappers.
As another answer here states, you write a basic class with data you need in UI and extend it with other model data, however this is not a good approach for several reasons.
If violates separation of concerns. Project dealing with model and persistance should not know about your ViewModel
You may need to flatten data from related objects into ViewModel objects. In that case, your model objects would have fields which should not be there, or would be redundant.
You may need calculated fields and helper methods in ViewModel which would again end up in model, confusing everyone that is not updated about design.
You could want to adopt several unrelated model classes to same ViewModel class
To try and put it shortly, either reuse model class or create ViewModels. There is unfortunately no clever solution. If you find one, please post a comment as I'd like to hear about it :)
I have picked LINQ to SQL as ORM framework for ASP .NET MVC3 project. Everything was good before I was faced with need to put additional field 'Confirm Password' to registration form. As it was mentioned in one question on SO (unfortunately I can't find it at the moment), it's better to use interface to extend generated LINQ to SQL classes with validation attributes, instead of having another class for storing validation attributes. So here we go:
public interface IRegTry
{
[Required]
[Email]
string EMail { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Should not exceed 100 symbols")]
string FirstName { get; set; }
[Required]
string Password { get; set; }
}
[MetadataType(typeof(IRegTry))]
public partial class RegTry : IRegTry { }
RegTry class is generated class by LINQ to SQL based on database entity.
On the View we have confirm password field, which should make sure that two typed password equals to each other.
So here we adding it:
public class RegTryViewModel : RegTry
{
[Required]
[EqualTo("Password", ErrorMessage = "You should type two identical passwords to continue")]
public string ConfirmPassword { get; set; }
}
View is strongly typed view with RegTryViewModel model.
I just ask here to make sure I'm doing everything right. The thing that makes me feel uncomfortable is that I spread validation logic between IRegTry interface and the RegTryViewModel class. But I can't add ConfirmPassword property to IRegTry interface because base SQL to LINQ class doesn't has it at all.
Thanks in advance guys!
I know that you already accepted an answer for this but I think it may be better to set this up using partial classes. As long as you set up the partial class in the same namespace with the same name, everything will get set up automatically. Here is an example of how I set this up in one of my projects:
namespace OperationsMetrics
{
[MetadataType(typeof(ClientStatMD))]
public partial class client_wkly_stat : IValidatableObject
{
public class ClientStatMD
{
[Required(ErrorMessage = "Client selection is required")]
public virtual int client_id { get; set; }
[Required(ErrorMessage = "SLAs met is required")]
public virtual int wkly_sla_met { get; set; }
[Required(ErrorMessage = "Total SLAs possible is required")]
public virtual int wkly_sla_req { get; set; }
[Required(ErrorMessage = "Number of input files is received")]
public virtual int num_inp_files_rec { get; set; }
[Required]
public string client_name { get; set; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (wkly_sla_met > wkly_sla_req)
{
yield return new ValidationResult("SLAs met cannot be greater that SLAs possible");
}
}
public string client_name { get; set; } //this isn't a part of the actual db object but can still be accessed in the Validate method
}
}
You can set up the Partial Class as an IValidatableObject which implements its own Validate method. You can have a check for Confirm==Password in your Validate method.
You can get some more information in this Pluralsight Video
If you are using View Model classes, you don't need validation logic connected to your DAL Model classes, so you shouldn't need that validation interface linked to the DAL Model class.
This is a minor question, more of a curiosity. When creating MetaData class for Model Validation, what is the recommended variable type to use for each property.
In the MSDN example, they use Object for all properties
[MetadataType(typeof(ProductMD))]
public partial class Product {
public class ProductMD {
[StringLength(50),Required]
public object Name { get; set; }
}
}
Other examples online use the same type as the model:
[MetadataType(typeof(ProductMD))]
public partial class Product {
public class ProductMD {
[StringLength(50),Required]
public String Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime ArrivalDate { get; set; }
}
}
Does the property type matter?
The type does not matter, that's why you can just use Object. Properties must match by name.