So I used this tutorial to generate my poco classes which I am to use throughout my aplication.. the problem is that Im not supposed to modify the generated cs files cause they get autoregenerated... How do I add attributes like [Required] and stuff like that?? please help
You can't add it directly (unless you modify T4 template to create them for you) but you can try to use trick introduced in ASP.NET dynamic data. All POCO classes are defined as partial. So lets define your partial part:
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(MyClassMetadata))]
public partial class MyClass
{
private class MyClassMetadata
{
[Required]
public object Id;
[Required]
[StringLength(100)]
public object Name;
}
}
Metadata class is special type to hold only metadata - it is never used. Name of fields must be same as corresponding fields in real class (field types doesn't matter so you can use object).
Anyway in ASP.NET MVC you should use specialized View model for each view and pass data you need so the validation attributes will be placed in view model class.
The attributes on the generated POCOs are derived from the facets on the entities in the model. e.g. for [Required] make sure the field is "not null" and for [StringLength(n)] make sure the datatype is nvarchar(n) via the MaxLength facet.
Further expanding on the answer. By using Microsoft Patterns & Practices Enterprise Library 5 Validation Block, you can open up a wealth of validation possibilities beyond those available through normal data annotations.
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
[HasSelfValidation]
public partial class Category : ICategory
{
[SelfValidation]
public void Validate(ValidationResults validationResults)
{
if (this.Title === "Credo")
{
validationResults.AddResult(
new ValidationResult(
"Category title cannot be a veiled reference to a former cool 2000AD character.",
this,
null,
null,
null));
}
validationResults.AddAllResults(
ValidationFactory
.CreateValidator<ICategory>()
.Validate(this));
}
}
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public interface ICategory
{
int Id
{
get;
set;
}
[Required]
[StringLengthValidator(1, 50, MessageTemplate = "Category title should be a maximum of 50 characters in length.")]
string Title
{
get;
set;
}
}
Related
Details:
ASP.Net MVC 5
.Net 4.5
MVVM
Entity Framework 6 Code First
I have ViewModels with complex properties on them. I have read about how I can use the BindAttribute to include or exclude properties for model bidning and got basic properties working with this.
However one thing I cannot find is how to control the binding of child properties within a collection property. For example I have the following
[Bind(Include = "Model.Id, Model.Positions.StartDate")]
public class ProjectViewModel
{
public Project Model {get;set;}
public ProjectViewModel(Project project)
: base(project)
{
Model = project;
}
public ProjectViewModel()
{
}
}
A Project has a list of Positions:
public class Project : BaseEntity
{
.
.
.
public virtual IList<Position> Positions
{
get;
set;
}
}
And a Position has a start and end date:
public class Position : BaseEntity
{
[SensibleDateTime]
public DateTime StartDate { get; set; }
[SensibleDateTime]
public DateTime EndDate { get; set; }
[Required]
[ForeignKey("Project")]
public Int64 ProjectID { get; set; }
public virtual Project Project { get; set; }
}
I have a screen using the Project View Model with a grid of positions where the user is allowed to change the start and end date of those positions. I do not want to allow them to let them amend any other properties of the Position including by manipulating the request / post to the server.
I am writing my own custom model binder which inherits from the DefaultModelBinder which gets the original entity out the database. I just want to bind permitted bound fields on top to get these new values and all the original values will already exist.
I am aware I could get the original entity out the database inside the controller instead and map the bound start and end date onto the entity manually. I want to avoid this if possible and make the controller code as simple as possible as this will be a very common task. Additionally it seems like using standard MVC mechanisms would be the preferred approach.
I have tried the following bind statement on the Project View Model:
[Bind(Include = "Model.Positions.StartDate")]
None of the new values are bound including the start date of the positions. If I write a simple include for a property directly on the Project View Model or Model it works.
How do you write a Bind attribute statement that refers to a property within an entity collection?
I can't say for sure as I never use the Bind attribute, but the dot syntax should work. However, I'm reasonably sure that Bind focuses on the posted name, not the actual model property. In other words, you would most likely need to do something like "Positions[0].StartDate,Positions[1].StartDate,...".
In general, it's far better to just use view models to only include the properties you want to be editable.
We are using EF4 database first approach to create all the entities as found in the context class. I'm now trying to add a display name attribute to one of the objects' properties as follows:
[MetadataType(typeof(OpportunityMetaData))]
public partial class Opportunity : EntityObject
{
}
public class OpportunityMetaData
{
[Display(Name = "Worked By")]
public int WorkedById { get; set; }
}
Then on a test page, using reflection, I'm trying to get an output that says "Worked By", as follows:
var attrType = typeof(DisplayNameAttribute);
var property = typeof(Opportunity).GetProperty("WorkedById");
Response.Write(((DisplayNameAttribute)property.GetCustomAttributes(attrType, false).FirstOrDefault()).DisplayName);
But this just gives Object Reference not set to an instance of an object. Alternatively, if I just Response.Write the property, it writes out "WorkedById" and not "Worked By".
Any help would be appreciated.
Its DisplayAttribute, not DisplayNameAttribute. Name is just a property on it.
I would like to know if it's possible to bypass the validation of one property which is using Data Annotations. Since I use the model across multiple pages, there's a check I need in some, but not in others, so I would like it to be ignored.
Thaks!
You could use FluentValidation, which uses as external validator class. In this case you would implement a different validator class for each scenario.
http://fluentvalidation.codeplex.com/
Example:
using FluentValidation;
public class CustomerValidator: AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotEmpty();
RuleFor(customer => customer.Forename).NotEmpty()
.WithMessage("Please specify a first name");
}
}
public class CustomerValidator2: AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotEmpty();
}
}
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);
CustomerValidator2 validator2 = new CustomerValidator2();
ValidationResult results2 = validator2.Validate(customer);
results would have 2 validation errors
results2 would have 1 validation error for the same customer
I don't believe this is possible with Data Annotations. I know the Microsoft Enterprise Library Validation Application Block has the notion of rule sets to group validations. This allows you to validate an object on several rule sets, for instance the default ruleset and on some pages the extended rule set. Data Annotations does not have something like rule sets.
Here is an example using VAB:
public class Subscriber
{
[NotNullValidator]
[StringLengthValidator(1,200)]
public string Name { get; set; }
[NotNullValidator(Ruleset="Persistence")]
[EmailAddressValidator]
public string EmailAddress { get; set; }
}
Say you had a config object
public class MyConfig{
public int PageSize{get;set;}
public string Title{get;set;}
}
and you want to automatically generate a asp.net form to edit the properties on this object.
Do you know of any frameworks to do this automagically?
I know of MS Dynamic data, but seems I need to have the whole stack (database, linq, objects) to get this up and running. So I was thinking of something simpler..
Sorry for jumping in late. There are several ways to use Dynamic Data with POCO.
Use the DynamicObjectDataSource which is found in Futures and Preview releases of Dynamic Data, starting with July 2008 Futures. When looking in a Preview release, it contains a Futures assembly, Microsoft.Web.DynamicData.dll.
When using ASP.NET 4.0 (now in Beta), you can call a new extension method, EnableDynamicData(). See the "SimpleDynamicDataSamples" project that comes with DD Preview 4 and later.
Here's an example from that code that uses an ObjectDataSource and the POCO class called "Product".
[MetadataType(typeof(Product.Metadata))]
public partial class Product {
public class Metadata {
[Required]
public string ProductName { get; set; }
[Range(0, 100)]
public decimal UnitPrice { get; set; }
}
}
public partial class ObjectDataSourceSample : System.Web.UI.Page {
protected void Page_Init() {
// Extension method syntax
ProductsList.EnableDynamicData(typeof(Product));
// Explicit syntax
// MetaTable table = MetaTable.CreateTable(typeof(Product));
// MetaTable.MapControl(ProductsList, table);
// ProductsList.ColumnsGenerator = new DefaultAutoFieldGenerator(table);
}
}
I was under the impression that you could modify the T4 templates used by dynamic data (Not sure if you can remove the data access part).
Have you looked at just using T4 on its own.
Does anybody know if it is possible to choose the order of the fields in Dynamic Data (of course, without customizing the templates of each table) ?
Thanks !
In .NET 4.0, using the 4.0 release of the Dynamic Data dll, you can set data annotations like so:
[Display(Name = " Mission Statement", Order = 30)]
public object MissionStatement { get; set; }
[Display(Name = "Last Mod", Order = 40)]
public object DateModified { get; private set; }
As per this thread - you can use the ColumnOrderAttribute in the dynamic data futures dll. You can grab the futures from codeplex.
You can do this by modifying the order of the public properties in your LINQ to SQL file.
For example, I went into Northwind.designer.cs which was my auto-generated LINQ to SQL file and moved the public property named Products above the public property CategoryName in the public partial class Category. Then I recompiled and the default template displayed the columns in my new order.
Of course, this means your editing auto-generated code and if you regenerate it, your changes are lost, so this technique is not without peril.
You have to create a custom page in DynamicData folder.
Here are the steps:
Create a folder that is the same name as your table name that you want to customize the ordering of columns under "DynamicData\CustomPages" folder
Create a custom page under "DynamicData\CustomPages\[folder with table name]" folder.
I just copy the existing "List.aspx" file from "DynamicData\PageTemplates" into the folder above.
Open the aspx file and modify GridView control to "AutoGenerateColumns='false'"
Inside columns section of GridView, add "DynamicControl" controls with the "DataField" attribute value to the name of your column in the order you want.
Here is a screencast from ScottHa:
http://www.asp.net/learn/3.5-SP1/video-293.aspx
GridView have ColumnsGenerator property, use it by implementing GenerateFields method of IAutoFieldGenerator interface in which you can set fields orders based on your custom rules (attributes, meta info, ...)
protected override void OnInit(EventArgs e)
{
...
this.gvItemsList.ColumnsGenerator = new EntityFieldsGenerator(CurrentDataSource.CurrentTableMetadata);
...
}
public class EntityFieldsGenerator : IAutoFieldGenerator {
...
public ICollection GenerateFields(Control control)
{
// based on entity meta info
var fields = from item in this.entityMetadata.Columns
where this.IncludeColumn(item.Value)
orderby item.Value.Order
select new DynamicField
{
DataField = item.Value.Column.Name,
HeaderText = item.Value.DisplayName,
DataFormatString = item.Value.DataFormatString,
UIHint = GetColumnUIHint(item.Value)
};
return fields.ToList();
} }
To avoid using the Dynamic Data futures dll, you can roll your own ColumnOrder attribute as follows:
[AttributeUsage(AttributeTargets.Property)]
public class ColumnOrderAttribute : Attribute
{
public int Order { get; private set; }
public ColumnOrderAttribute() { Order = int.MaxValue; }
public ColumnOrderAttribute(int order) { Order = order; }
public static ColumnOrderAttribute Default = new ColumnOrderAttribute();
}
and then in your class that implements IAutoFieldGenerator, you have
public static class ExtensionMethods
{
public static int GetOrder (this MetaColumn column)
{
var orderAttribute = column.Attributes.OfType<ColumnOrderAttribute>().DefaultIfEmpty(ColumnOrderAttribute.Default).Single();
return orderAttribute.Order;
}
}
public ICollection GenerateFields(Control control)
{
var fields = new List<DynamicField>();
var columns = _table.Columns.OrderBy(column => column.GetOrder());
foreach (var column in columns)
{
if (!column.Scaffold) { continue; }
fields.Add(new DynamicField {DataField = column.Name});
}
}
and finally your usage would look like
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer {}
public class CustomerMetadata
{
[ColumnOrder(1)]
public object FirstName {get;set;}
[ColumnOrder(2)]
public object LastName {get;set;}
}
I'm answering an old question because it seems to me that the possible solution changed in newer versions of the framework.
It seems that the Display(Order) works now directly as asked (Visual Web Developer 2010 on .NET 4.0) without any particular workaround.
Example:
[Display(Order = 50)]
An important thing it's to check the correct object name to map the foreignkey:
in one project a field OperatoreID translated in the entity class as:
public object Operatore { get; set; }
being Operatore the source table of the foreignkey; for a second reference on the same table it will get something like 1 and so on.