AddAttributes not having an effect

If I put this in LINQPad, the attribute doesn’t seem to have any effect.

var dbFactory = new OrmLiteConnectionFactory(this.Connection.ConnectionString, SqlServer2017Dialect.Provider);
var dbc = dbFactory.DialectProvider.GetStringConverter();
dbc.UseUnicode = true;

typeof(ServiceStack.Configuration.ConfigSetting)
	.GetProperty("Id")
	.AddAttributes(new ServiceStack.DataAnnotations.StringLengthAttribute(256));

using (var db = dbFactory.OpenDbConnection())
{
	db.CreateTable<ServiceStack.Configuration.ConfigSetting>();
}

Any idea why the “AddAttributes” doesn’t take effect? The “Id” column is created with nvarchar(4000) instead of nvarchar(256). Same behavior if I do it to the “Value” column.

Don’t know, works fine in Gistlyn:

https://gistlyn.com/?gist=4626283b302cf1b7f87685aea8b8a2ee&collection=991db51e44674ad01d3d318b24cf0934

You can also try modifying the metadata directly, e.g:

typeof(ServiceStack.Configuration.ConfigSetting).GetModelMetadata()
    .PrimaryKey.FieldLength = 256;

Yeah, weird. Not sure why it’s working on Gistlyn. I thought maybe it was related to using .net core, but it’s the same behavior in both linqpad 5 & 6 (i’m using the 5.8.1 nuget packages, not sure that would make a difference).

Here’s a little more demo of the issue I’m seeing, sorry about the screenshots here, but a little bit easier to convey the point.
Maybe it has something to do with when the ModelDefinition is made “static”.

  • For example, without the call to GetModelMetadata, the StringLengthAttribute is ignored:

ss_configsetting_add_attribute_without_getmodelmetadata

  • If adding the StringLengthAttribute is done AFTER the call to GetModelMetadata method, then the attribute is also ignore

ss_configsetting_add_attribute_after_getmodelmetadata

  • If adding the StringLengthAttribute is done BEFORE the call to GetModelMetadata method, then the attribute is NOT ignored (although, it’s a bit weird here that the create table statement reverses the order of the columns as well

ss_configsetting_add_attribute_before_getmodelmetadata

  • Which got me thinking that it may be related to when the ModelDefinition is actually being analyzed, so I changed the GetModelMetadata method to just getting the definition of the model using ModelDefinition<T>, and this DOES work, but again, it has to be done AFTER the StringLengthAttribute is added, not before

Maybe I’ll try to write a console app just to see if it’s linqpad related or not.

Based on all this, it seems that the most reliable way to guarantee the desired behavior would be then to use GetModelMetadata or something like the following, instead of trying to understand the proper order to adding the dynamic attributes (for the time being at least).

var def = ModelDefinition<ConfigSetting>.Definition;
// make explicit changes
def.GetFieldDefinition("Value").FieldLength = 1024;

I don’t get the same behavior in a console app, so there does seem to be an issue in LINQPad specifically. When no call to GetModelMetadata and/or ModelDefinition is made it seems, in LINQPad the attribute is ignored, but in the console app it works. However …

The issue that is common to both is that IF a call to typeof(T).GetModelMetadata and/or ModelDefinition<T> is made, that seems to lock in the model definition, and adding attributes to T dynamically doesn’t work after that point.

See: https://gist.github.com/mattjcowan/4c4997c00578e35815d7e9969a2bd861

So for me at least right now, it seems the only reliable way to really add/change the model at runtime is to alter the model directly with ModelDefinition<T>.Definition.{do stuff here}, unless you can easily control and observe that nothing in the codebase will access the model definition before you add attributes to the model. Anyway, this is a workaround, and should work for me.

I wonder if when using AddAttributes and similar calls, it could invalidate the ModelDefinition so that it gets re-analyzed later properly.

That’s exactly how it works, the metadata for the table is created once on startup and should remain the immutable there after, any dynamic attributes should be added before using OrmLite and before AppHost Configure() is run as most things are cached to avoid runtime reflection.

I understand why. I’ll work around it.