My requirement is configuing the string length mapping globally, but also can use MaxLengthAttribute to configue a property specially. Here's my code:
public class StringLengthConvention
: IConfigurationConvention<PropertyInfo, StringPropertyConfiguration>
{
public void Apply(
PropertyInfo propertyInfo,
Func<StringPropertyConfiguration> configuration)
{
StringAttribute[] stringAttributes = (StringAttribute[])propertyInfo.GetCustomAttributes(typeof(StringAttribute),true);
if (stringAttributes.Length > 0)
{
configuration().MaxLength = stringAttributes [0].MaxLength;
}
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add<StringLengthConvention>();
}
public class ContentInfo
{
// ...
[MaxLength(200)]
[String]
public string TitleIntact { get; set; }
// ...
}
My problem is the "MaxLength" can't work anymore. Do i need to check if the property has a MaxLengthAttribute before applying the global configuration in the StringLengthConvention.Apply()?
What would work in this situation is to create a lightweight convention specifying the MaxLength property for strings. In this situation the convention would set the max length of the property for all strings except where it was already configured by an annotation, with the fluent API, or by another convention.
In your OnModelCreate method add the following code to set your default MaxLength:
modelBuilder.Properties<string>()
.Configure(c => c.HasMaxLength(DefaultStringLength));
there is a walkthough of conventions here: http://msdn.microsoft.com/en-us/data/jj819164.aspx
make sure to take a look at the "Further Examples" at the bottom of the page
Related
I'm trying to create an attribute with PostSharp to implement specific backing fields of properties. However I did not find any helpful answers in the documentation, official examples or here on SO.
Here's an example of what I'm trying to do:
[WrappedProperty]
public int MyProperty { get; set; }
will compile to
private WrapperClass<int> _generatedBackingField_myProperty;
public int MyProperty
{
get => _generatedBackingField_myProperty.Value;
set => _generatedBackingField_myProperty.Value = value;
}
Is there any way to achieve this with PostSharp?
I found the answer, you can use LocationInterceptionAspect to intercept properties.
So the code would look like this:
[PSerializable]
public class WrappedProperty : LocationInterceptionAspect
{
private WrapperClass<object> _backingField;
public override void OnGetValue(LocationInterceptionArgs args)
{
InitBackingField();
args.Value = _backingField.Value;
}
public override void OnSetValue(LocationInterceptionArgs args)
{
InitBackingField();
_backingField.Value = args.Value;
}
}
I am using normal helper EditFor to bind data to html controls.
For completeness, here a example:
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #maxlength= 100, #class = "form-control" } })
To set maxlength for string fields, i need to explicitly set the attribute in every view that uses the same table field and there are many views and strings value in the data model.
Doing this in every page is error prone. Changes is size will break the app if a place is forgotten.
How to pass the length directly from EF Model?
The easiest option is to add the [MaxLength(50)] attribute to the model property. For example:
public class SomeModel
{
[MaxLength(50)]
public string SomeProperty { get; set; }
}
You can then omit the maxlength property from the call to #Html.EditorFor() and it will be taken from the model metadata.
You can use the Fluent API to configure a maximum length for a property. In this example, targeting SQL Server this would result in the nvarchar(500) data type being used.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Test>()
.Property(b => b.Description)
.HasMaxLength(500);
}
Or You can use the Data Annotations to configure a maximum length for a property.
public class Test
{
[MaxLength(500)]
public string Description{ get; set; }
}
What is the feasibility of modifying the mapping code to convert a short of value zero or non-zero to false or true, if the boolean destination property is marked with an attribute in the POCO model?
I mean, this is supposed to be one of the advantages of EF being open sourced, and would be for in house use only.
Any tips on where in the code I would look would be appreciated, but this question is really more general and I'd like to hear anything anyone has to say on this.
With regard to the General comments please.
I dont know to make the EF change, but dealing with similar issues is not an uncommon issue in EF.
Not all standard types are supported by EF.
You can have a helper field in your POCO class.
So one field is the actual DB field, but no used outside of POCO.
The help field is NOTMAPPED or ignored in fluent API.
You access the DB via you helper and execute any required casting.
A simple example. Or the reverse if I got helper and DB field types back to front.
[NotMapped]
public virtual bool IsVisible { set; get; } // Helper Field NOT on DB
public int Test { get { return IsVisible ? 1 : 0; } // on DB, but set and get via helper only.
set { IsVisible = (value != 0); } }
Edit: Power Fluent API
Here is a snippet that outlines how you have code that runs for every mapped poco in a consistent way.
public class MyDbContext : DbContext
// model building, set breakpoint so you know when this is triggered
// it is important this ISNT called everytime, only on model cache.
// in my case that is app pool recycle.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
// use the CONFIG add feature to better organize and allow use of inheritance when mapping
// I will use snippets and statics to keep it simple.
modelBuilder.Configurations.Add(XYZMap.Map()); // POCO map
modelBuilder.Configurations.Add(ABCMAP.Map()); // poco map
modelBuilder.Configurations.Add(XXXMap.MAP()); // poco map
// etc for your POCO set
// Note, no need to declare DBset<xyz> XYZs {get;set;} !!!!
public static class XYZMap {
public static BaseEntityIntConfiguration<PocoXYZ> Map() {
//see return object !
var entity = new BaseEntityLongConfiguration<PocoXYZ>();
//entity.Property()... // map away as usual POCO specifc
///entity.HasRequired()...// property and relationships as required
// do nothing for default
return entity;
}
}
}
// all tables with int key use this base config. do it once never again
public class BaseEntityIntConfiguration<T> : BaseEntityConfiguration<T> where T : BaseObjectInt {
public BaseEntityIntConfiguration(DatabaseGeneratedOption DGO = DatabaseGeneratedOption.Identity) {
// Primary Key
this.HasKey(t => t.Id);
// Properties
//Id is an int allocated by DB
this.Property(t => t.Id).HasDatabaseGeneratedOption(DGO); // default to db generated
// optimistic lock is also added here, Specific to out poco design
this.Property(t => t.RowVersion)
.IsRequired()
.IsFixedLength()
.HasMaxLength(8)
.IsRowVersion();
// any other common mappings/ rules ??
}
}
public class BaseEntityConfiguration<T> : EntityTypeConfiguration<T> where T : BaseObject {
public BaseEntityConfiguration() {
this.ApplyAttributeRules(); // <<<<< Here is where I apply SYSTEM WIDE rules
}
}
public static void ApplyAttributeRules<T>(this EntityTypeConfiguration<T> entity) where T : BaseObject {
// so this will be called for each mapped type
foreach (var propertyInfo in typeof (T).GetProperties()) {
// I use reflection to look for properties that meet certain criteria.
// eg string. I want as NVARCHAR 4000 not NVCAHR max so i can index it.
if (propertyInfo.UnderLyingType().FullName == "System.String") {
SetStringLength(BosTypeTool.StringLengthIndexable, propertyInfo.Name, entity);
continue;
}
SetStringLength(4000, propertyInfo.Name, entity);
}
}
private static void SetStringLength<TModelPoco>(int length, string propertyName,
EntityTypeConfiguration<TModelPoco> entity) where TModelPoco : BaseObject {
var propLambda = DynamicExpression.ParseLambda<TModelPoco, String>(propertyName);
entity.Property(propLambda).HasMaxLength(length);
// dynamic library from Microsoft.... http://msdn.microsoft.com/en-US/vstudio/bb894665.aspx
}
// get underlying type incase it is nullable
public static Type UnderLyingType(this PropertyInfo propertyInfo) {
return Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
}
Very minor thing really but it bugs me slightly so I thought I'd ask. I have the POCO entity Setting and I'm using a code first approach to Entity Framework.
public class Setting
{
[Required]
[MaxLength(128)]
public string Name { get; set; }
[Required]
public Type Type { get; set; }
// Added to support the storing of Type in the database via Entity Framework.
// Really would be nice to find a cleaner way but this isn't actually so bad.
public string TypeString
{
get { return Type.ToString(); }
set { Type = Type.GetType(value); }
}
public string Value { get; set; }
}
As you can see for use in code I'd like to actually be using the Type object but to store this I have ended up adding a TypeString property. Via the DbModelBuilder I then hide the Type property.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<Setting>()
.HasKey(e => e.Name)
.Property(e => e.Name)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder
.Entity<Setting>()
.Ignore(e => e.Type);
modelBuilder
.Entity<Setting>()
.Property(e => e.TypeString)
.HasColumnName("Type");
base.OnModelCreating(modelBuilder);
}
I just was wondering if there was a way of defining a custom property mapping instead of having to add that extra property to my entity.
UPDATE
My reasoning behind these was actually that I just wanted a quick and easy way for developers to be able to configure a few simple settings by logging in, and it was late and this seemed like a quick solution to allow for several settings of various types.
I suppose if if I wanted some strongly typed settings I'd probably look at a generic implementation of setting such as below:
public class Setting<T>
{
[Required]
[MaxLength(128)]
public string Name { get; set; }
public T Value { get; set; }
}
Though I don't believe that is something that will play nice with Entity Framework.
In part though I'm also curious as for some applications I have multiple clients or stakeholders who can each request slightly different validation rules. As such we usually implement and interface and create an implementation per clients or collections of clients. In order that we can more easily add clients and customise their rules we store which implementation of the interface to create for each client. So persisting type information has proved extremely useful in those cases.
Also it's nice to just explore and understand ways that I can quite happily develop an application whilst reducing the need to think how am I going to persist this, or is this going to play nice with Entity Framework as much as possible.
I'm not aware of any way to persist Type directly, but this may feel a bit better:
public class Settings
{
public Type Type
{
get { return Type.GetType(_TypeString); }
set { _TypeString = value.ToString(); }
}
// Backing Field
protected virtual string _TypeString { get; set; }
}
Then you just need to map the protected _TypeString property (solution from here):
public static StringPropertyConfiguration Property<T>(this EntityTypeConfiguration<T> mapper, String propertyName) where T : class
{
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(propertyName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
LambdaExpression lambda = Expression.Lambda(expr, arg);
Expression<Func<T, String>> expression = (Expression<Func<T, string>>)lambda;
return mapper.Property(expression);
}
Then, in your ModelBuilder:
modelBuilder
.Entity<Setting>()
.Property("_TypeString")
.HasColumnName("Type");
With FubuMVC, I'm not sure what the best way is to determine the current action's output model type. I see different objects that I could get the current request's URL from. But that doesn't lead to a very good solution.
What's the easiest way to get the current action's output model type from the behavior?
If this isn't a good practice, what's a better way?
First, I'm assuming you've already got your settings object(s) set up in StructureMap and have the ISettingsProvider stuff already wired up.
The best, simplest thing to do would be just to pull the settings in the view, like this:
<%: Get<YourSettingsObject>().SomeSettingProperty %>
If you insist on having these be a property on your output model, then continue reading:
Let's say you had a settings object like this:
public class OutputModelSettings
{
public string FavoriteAnimalName { get; set; }
public string BestSimpsonsCharacter { get; set; }
}
Then you had an output model like this:
public class OutputModelWithSettings
{
public string SomeOtherProperty { get; set; }
public OutputModelSettings Settings { get; set; }
}
You'll need to do a few things:
Wire up StructureMap so that it will do setter injection for Settings objects (so it will automatically inject the OutputModelSettings into your output model's "Settings" property.
Set up a setter injection policy in your StructureMap initialization code (a Registry, Global ASAX, your Bootstrapper, etc -- wherever you set up your container).
x.SetAllProperties(s => s.Matching(p => p.Name.EndsWith("Settings")));
Create your behavior to call StructureMap's "BuildUp()" on the output model to trigger the setter injection. The behavior will be an open type (i.e. on the end) so that it can support any kind of output model
public class OutputModelSettingBehavior<TOutputModel> : BasicBehavior
where TOutputModel : class
{
private readonly IFubuRequest _request;
private readonly IContainer _container;
public OutputModelSettingBehavior(IFubuRequest request, IContainer container)
: base(PartialBehavior.Executes)
{
_request = request;
_container = container;
}
protected override DoNext performInvoke()
{
BindSettingsProperties();
return DoNext.Continue;
}
public void BindSettingsProperties()
{
var viewModel = _request.Find<TOutputModel>().First();
_container.BuildUp(viewModel);
}
}
Create a convention to wire up the behavior
public class OutputModelSettingBehaviorConfiguration : IConfigurationAction
{
public void Configure(BehaviorGraph graph)
{
graph.Actions()
.Where(x => x.HasOutput &&
x.OutputType().GetProperties()
.Any(p => p.Name.EndsWith("Settings")))
.Each(x => x.AddAfter(new Wrapper(
typeof (OutputModelSettingBehavior<>)
.MakeGenericType(x.OutputType()))));
}
}
Wire the convention into your FubuRegistry after the Routes section:
ApplyConvention<OutputModelSettingBehaviorConfiguration>();
In your view, use the new settings object:
<%: Model.Settings.BestSimpsonsCharacter %>
NOTE: I have committed this as a working sample in the FubuMVC.HelloWorld project in the Fubu source. See this commit: https://github.com/DarthFubuMVC/fubumvc/commit/2e7ea30391eac0053300ec0f6f63136503b16cca