1

Closed

Having System.ComponentModel.DataAnnotations.MaxLength() applied to a string property leads to InvalidCastException exception

description

Hi,

I have just tried 6.0.1 version of EF, and it is broken: InvalidCastException is thrown on a string type property annotated with MaxLength() attribute, as in:
    [DataType(DataType.MultilineText)]
    [MaxLength]
    [Column("AdditionalDataSerialized", TypeName = "nvarchar")]
    public virtual string AdditionalDataSerialized { get; set; }
It was working in the RC1 version of EF 6.

Following is the code which throws the exception:
    public static FacetValues Create(IEnumerable<Facet> facets)
    {
        FacetValues values = new FacetValues();
        foreach (Facet facet in facets)
        {
            switch (facet.Description.FacetName)
            {
                case "MaxLength":
                    values.MaxLength = (int?) facet.Value;
                    break;
See the (int?) typecast: it fails when the facet.Value is of type System.Data.Entity.Core.Metadata.Edm.EdmConstants+Unbounded.

Regards

Iouri
Closed Nov 22, 2013 at 8:40 PM by maumar
closing

comments

ajcvickers wrote Oct 25, 2013 at 4:37 PM

@Iouri I have not been able to repro this. Could you provide some code that repros the error--it may be some combination of things that is causing this to happen rather than just the MaxLength attribute. Also, two more questions: are you using .NET 4 or .NET 4.5, and what provider are you using? Finally, could you post the full stack trace from the exception?

Thanks,
Arthur

iouri wrote Oct 26, 2013 at 10:11 PM

Hi Arthur

I cant give you code to reproduce the issue as I don't have time for this at the moment.

Answers to your questions:
  1. .NET4, SQL Server
    2.
EntityFramework.dll!System.Data.Entity.Core.Metadata.Edm.FacetValues.Create(System.Collections.Generic.IEnumerable<System.Data.Entity.Core.Metadata.Edm.Facet> facets = Count = 1) Line 37 + 0x36 bytes C#
EntityFramework.dll!System.Data.Entity.Core.Metadata.Edm.EdmProperty.PrimitiveType.set(System.Data.Entity.Core.Metadata.Edm.PrimitiveType value = {System.Data.Entity.Core.Metadata.Edm.PrimitiveType}) Line 409 + 0x1b bytes   C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.EdmProperty column = {System.Data.Entity.Core.Metadata.Edm.EdmProperty}, System.Data.Entity.Core.Metadata.Edm.EntityType table = {System.Data.Entity.Core.Metadata.Edm.EntityType}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, bool allowOverride = false, bool fillFromExistingConfiguration = false) Line 131    C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.Configure.AnonymousMethod__0(System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType> pm = {System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>}) Line 105 + 0x57 bytes    C#
EntityFramework.dll!System.Data.Entity.Utilities.IEnumerableExtensions.Each<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>>(System.Collections.Generic.IEnumerable<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>> ts = {System.Linq.Enumerable.WhereListIterator<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>>}, System.Action<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>> action = {Method = {System.Reflection.RuntimeMethodInfo}}) Line 27 + 0x49 bytes   C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.Configure(System.Collections.Generic.IEnumerable<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>> propertyMappings = {System.Linq.Enumerable.WhereListIterator<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>>}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, bool allowOverride = false, bool fillFromExistingConfiguration = false) Line 106   C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Types.StructuralTypeConfiguration.ConfigurePropertyMappings(System.Collections.Generic.IList<System.Tuple<System.Data.Entity.Core.Mapping.ColumnMappingBuilder,System.Data.Entity.Core.Metadata.Edm.EntityType>> propertyMappings = Count = 11, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, bool allowOverride = false) Line 101 + 0xb9 bytes  C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigurePropertyMappings(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping databaseMapping = {System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping}, System.Data.Entity.Core.Metadata.Edm.EntityType entityType = {System.Data.Entity.Core.Metadata.Edm.EntityType}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, bool allowOverride = true) Line 323 C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigurePropertyMappings(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping databaseMapping = {System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping}, System.Data.Entity.Core.Metadata.Edm.EntityType entityType = {System.Data.Entity.Core.Metadata.Edm.EntityType}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, bool allowOverride = false) Line 324 + 0x8b8 bytes  C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.EntityType entityType = {System.Data.Entity.Core.Metadata.Edm.EntityType}, System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping databaseMapping = {System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}) Line 146    C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntityTypes(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping databaseMapping = {System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}) Line 170 + 0x295 bytes  C#
EntityFramework.dll!System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping databaseMapping = {System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping}, System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}) Line 120   C#
EntityFramework.dll!System.Data.Entity.DbModelBuilder.Build(System.Data.Entity.Core.Common.DbProviderManifest providerManifest = {System.Data.Entity.SqlServer.SqlProviderManifest}, System.Data.Entity.Infrastructure.DbProviderInfo providerInfo = {System.Data.Entity.Infrastructure.DbProviderInfo}) Line 103   C#
EntityFramework.dll!System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection providerConnection = {System.Data.SqlClient.SqlConnection}) Line 73 + 0xf bytes C#
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.CreateModel(System.Data.Entity.Internal.LazyInternalContext internalContext = {System.Data.Entity.Internal.LazyInternalContext}) Line 53 + 0x49 bytes   C#
EntityFramework.dll!System.Data.Entity.Internal.RetryLazy<System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel>.GetValue(System.Data.Entity.Internal.LazyInternalContext input = {System.Data.Entity.Internal.LazyInternalContext}) Line 30 + 0x14 bytes   C#
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.InitializeContext() Line 133 + 0x8c bytes   C#
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.Initialize() Line 355   C#
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.ForceOSpaceLoadingForKnownEntityTypes() Line 297 + 0x9 bytes    C#
EntityFramework.dll!System.Data.Entity.DbContext.System.Data.Entity.Infrastructure.IObjectContextAdapter.ObjectContext.get() Line 229   C#
XYZ.EF.dll!Janison.EF.JanisonEfExtensions.UnderlyingObjectContext(System.Data.Entity.DbContext oc = {JService.Entities}) Line 46 + 0x9 bytes    C#

Regards

Iouri

iouri wrote Oct 28, 2013 at 12:26 AM

Hi Arthur

Here is more information for you.

I wasnt able to reproduce the issue by creating a simple model hosted in a simple console application.

It is possible that the issue is around the fact that in our application we have to handle both ms sql and ms sql compact, therefore we reference providers for both types: EntityFramework.SqlServer.dll and EntityFramework.SqlServerCompact.dll.

I've noticed the following code in EdmProperty.PrimitiveType setter:
            foreach (FacetDescription description in value.GetAssociatedFacetDescriptions())
            {
                Facet facet;
                if (this.TypeUsage.Facets.TryGetValue(description.FacetName, false, out facet) && (((facet.Value == null) && (facet.Description.DefaultValue != null)) || ((facet.Value != null) && !facet.Value.Equals(facet.Description.DefaultValue))))
                {
                    facets.Add(facet);
                }
            }
The problem seems to be around the fact that for nvarchar(max) property, this.TypeUsage.Facets contains MaxLength facet with its Value set to {Max}, and its Description.DefaultValue set to 4000, therefore the facet isnt treated as a default one and gets added to the facets list, which later causes the above exception.

What do you think has changed since RC1 that causes this behaviour? It would be great if the issue was solved so that we could move to the 6.0.1 version.

Thanks

Iouri

iouri wrote Oct 28, 2013 at 8:51 AM

Hi Arthur,

I've investigated it further and found that having the Column() attribute applied to a property with MaxLength() attribute changes the size of the generated column to nvarchar(128) - instead of nvarchar(max) as expected.

As per following example:
class Program
{
    static void Main(string[] args)
    {
        using (var context = new MaxLengthPropertyTestDataContext(@"Data Source=(local)\sql2012;Initial Catalog=MaxLengthPropertyTestDb;Integrated Security=True"))
        {
            var entities = context.EntitiesWithMaxLengthProperty.ToList();

            Console.WriteLine("Finished; press any key to exit...");
            Console.ReadKey(true);
        }
    }
}

public class EntityWithMaxLengthProperty
{
    public virtual Guid Id { get; set; }
    //(IS) having this Column() attribute applied will result in nvarchar(128) column created 
    //(IS) when no Column() attribute is applied, correct nvarchar(max) column is created
    [System.ComponentModel.DataAnnotations.Schema.Column("Description", TypeName = "nvarchar")]
    [System.ComponentModel.DataAnnotations.MaxLength]
    public virtual string Description { get; set; }
}

public class MaxLengthPropertyTestDataContext : DbContext
{
    public MaxLengthPropertyTestDataContext(string connectionString) : base(connectionString) { }
    public virtual DbSet<EntityWithMaxLengthProperty> EntitiesWithMaxLengthProperty { get; set; }
}
While this issue isn't exactly as the one reported, I have a feeling that they are related.

Regards

Iouri

ajcvickers wrote Oct 30, 2013 at 5:14 PM

Notes for triage:

I have been unable to repro the original bug, but I think it is related to the use of the value “max” for the MaxLength facet of the SSDL. Based on the additional information in the bug there is also inconsistent interaction between partially specified column types (e.g. nvarchar with no parenthesis) and max length specifications, which may also be related to the use of the "Max" value.

Given that I can’t repro the original problem at this time I suggest attempting to fix the inconsistent interaction for which I have filed a new issue: https://entityframework.codeplex.com/workitem/1784. Fixing the interaction with Max may also fix this issue or it may be that the repro for this issue becomes apparent as part of that fix.

divega wrote Oct 31, 2013 at 8:16 AM

Assigning to Andriy for investigation as he is going to be looking at fixing related but #1784

emilcicos wrote Nov 15, 2013 at 7:43 PM

Fixed in changeset 225e9f4da98ebbf8025fc1aa98aea0f44b722728

emilcicos wrote Nov 15, 2013 at 9:47 PM

Fixed in changeset de0054f9666d7dae965b17b9d318197965d897d6