New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataAnnotations not working in UWP at design-time #5945

Closed
minimalisticMe opened this Issue Jul 1, 2016 · 31 comments

Comments

Projects
None yet
8 participants
@minimalisticMe

minimalisticMe commented Jul 1, 2016

I created the following minimal example, a new project in VS2015.3 on win10 with added dependencies and the App.Config like described in the tutorial. I called the project SQLMinimalTest. The following code is the only code I added to a fresh UWP template.

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Windows.UI.Xaml.Media;

namespace SQLMinimalTest
{
public class Blog
{
    [Key]
    public int BlogID { get; set; }
    public string RandomString { get; set; }

    [NotMapped]
    public SolidColorBrush Brush
    {
        get { return _Brush; }
        set { _Brush = value; }
    }
    [NotMapped]
    private SolidColorBrush _Brush;
}

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Filename=BloggingTest.db");
    }
}
}

When I now type Add-Migration mig1 -Verbose in Package Manager Console, I get the following Error:

PM> Add-Migration mig1 -Verbose
Using startup project 'SQLMinimalTest'.
Using project 'SQLMinimalTest'
Build started...
Build succeeded.
Using application base 'D:\Programmieren\C#\VS2015 Testprojects\SQLMinimalTest\SQLMinimalTest\bin\x86\Debug\'.
Using application configuration 'D:\Programmieren\C#\VS2015 Testprojects\SQLMinimalTest\SQLMinimalTest\App.Config'
Using current directory 'D:\Programmieren\C#\VS2015 Testprojects\SQLMinimalTest\SQLMinimalTest\bin\x86\Debug\'.
Finding DbContext classes...
Using context 'BloggingContext'.
System.InvalidOperationException: The key {'TempId'} contains properties in shadow state and is referenced by a relationship from 'SolidColorBrush' to 'Blog.Brush'. Configure a non-shadow principal key for this relationship.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.KeyConvention.Apply(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.Validate()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Validate()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass14_0.<GetModel>b__0(Object k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_4(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder.<>c__DisplayClass7_0.<ConfigureContextServices>b__9(IServiceProvider _)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_1.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The key {'TempId'} contains properties in shadow state and is referenced by a relationship from 'SolidColorBrush' to 'Blog.Brush'. Configure a non-shadow principal key for this relationship.

and the project.json:

{
  "dependencies": {
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0"
  },
  "frameworks": {
    "uap10.0": {}
  },
  "runtimes": {
    "win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}
  }
}

This is not the expected outcome. With the previous version I did not have this issue.

@smitpatel

This comment has been minimized.

Show comment
Hide comment
@smitpatel

smitpatel Jul 1, 2016

Contributor

It seems to be specific to UWP. Probably due to reflection. Do any other data annotations work?

Contributor

smitpatel commented Jul 1, 2016

It seems to be specific to UWP. Probably due to reflection. Do any other data annotations work?

@minimalisticMe

This comment has been minimized.

Show comment
Hide comment
@minimalisticMe

minimalisticMe Jul 2, 2016

Ah well, I thought [Key] was working, because I used capital letters ID instead of Id. But it doesn't. Assuming the follwing code:

public class Blog
{
    public int BlogID { get; set; }
    public string RandomString { get; set; }
    [Key]
    public int custom { get; set; }
}

yields the following migration:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Blogs",
        columns: table => new
        {
            BlogID = table.Column<int>(nullable: false)
                .Annotation("Autoincrement", true),
            RandomString = table.Column<string>(nullable: true),
            custom = table.Column<int>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Blogs", x => x.BlogID);
        });
}

I can tell, that [NotMapped] was working in the previous version of EF Core Sqlite and Tools.

minimalisticMe commented Jul 2, 2016

Ah well, I thought [Key] was working, because I used capital letters ID instead of Id. But it doesn't. Assuming the follwing code:

public class Blog
{
    public int BlogID { get; set; }
    public string RandomString { get; set; }
    [Key]
    public int custom { get; set; }
}

yields the following migration:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Blogs",
        columns: table => new
        {
            BlogID = table.Column<int>(nullable: false)
                .Annotation("Autoincrement", true),
            RandomString = table.Column<string>(nullable: true),
            custom = table.Column<int>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Blogs", x => x.BlogID);
        });
}

I can tell, that [NotMapped] was working in the previous version of EF Core Sqlite and Tools.

@smitpatel smitpatel changed the title from DataAnnotation [NotMapped] ignored by EF Core v1 to DataAnnotations not working UWP Jul 2, 2016

@smitpatel smitpatel changed the title from DataAnnotations not working UWP to DataAnnotations not working in UWP Jul 2, 2016

@rowanmiller rowanmiller added the type-bug label Jul 5, 2016

@rowanmiller rowanmiller added this to the 1.0.1 milestone Jul 5, 2016

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 5, 2016

Member

@natemcmaster we should understand the scope of this, and what the fix looks like, then discuss in triage whether it goes in 1.0.1

Member

rowanmiller commented Jul 5, 2016

@natemcmaster we should understand the scope of this, and what the fix looks like, then discuss in triage whether it goes in 1.0.1

@minimalisticMe

This comment has been minimized.

Show comment
Hide comment
@minimalisticMe

minimalisticMe Jul 5, 2016

Well if it helps going back to Microsoft.EntityFrameworkCore.Tools v1.0.0-preview1-final, Microsoft.EntityFrameworkCore.Sqlite v1.0.0-rc2-final and Microsoft.NETCore.UniversalWindowsPlatform v5.1.0 as well as deleting the file app.config does not solve the issue and I'm kinda stuck in development. I tried quite some stuff, but do not know a workaround yet.

minimalisticMe commented Jul 5, 2016

Well if it helps going back to Microsoft.EntityFrameworkCore.Tools v1.0.0-preview1-final, Microsoft.EntityFrameworkCore.Sqlite v1.0.0-rc2-final and Microsoft.NETCore.UniversalWindowsPlatform v5.1.0 as well as deleting the file app.config does not solve the issue and I'm kinda stuck in development. I tried quite some stuff, but do not know a workaround yet.

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 5, 2016

Member

@minimalisticMe if it helps in the meantime, there is a Fluent API equivalent to all data annotations, so maybe you can swap to using that - https://docs.efproject.net/en/latest/modeling/index.html.

Member

rowanmiller commented Jul 5, 2016

@minimalisticMe if it helps in the meantime, there is a Fluent API equivalent to all data annotations, so maybe you can swap to using that - https://docs.efproject.net/en/latest/modeling/index.html.

@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Jul 6, 2016

Member

@rowanmiller this seems pretty serious. Data annotations will not be discovered at design time, even though they will work at runtime.

Although I'm not sure what the solution is, here is the problem:

UWP apps at runtime execute on .NET Core (which uses System.ComponentModel.Annotations), but at design time we use the .NET Framework (System.ComponentModel.DataAnnotations). These assemblies are slightly different. This means at design-time, typeof(KeyAttribute) != typeof(KeyAttribute).

To see this more clearly, run this code at design-time. You'll see the conventions don't work because they are looking for the wrong data annotations types.

var output = "";
var keyAttrConventionTypeArg =
    typeof(KeyAttributeConvention).GetTypeInfo().BaseType.GetGenericArguments()[0];

output += "typeof(TAttribute) in KeyAttributeConvention:\n    " + keyAttrConventionTypeArg.AssemblyQualifiedName;

var keyAttrType = typeof(Blog).GetProperty(nameof(Blog.BlogID))
    .GetCustomAttributes(true)
    .Single(a => a.GetType().Name.Contains("KeyAttribute"))
    .GetType();
output += "\ntypeof(KeyAttribute) on Blog.BlogID:\n    " + keyAttrType.AssemblyQualifiedName;

output += "\nAre they equal? " + keyAttrType.Equals(keyAttrConventionTypeArg);
throw new Exception(output);

This outputs:

typeof(TAttribute) in KeyAttributeConvention:
    System.ComponentModel.DataAnnotations.KeyAttribute, System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
typeof(KeyAttribute) on Blog.BlogID:
    System.ComponentModel.DataAnnotations.KeyAttribute, System.ComponentModel.Annotations, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Are they equal? False
Member

natemcmaster commented Jul 6, 2016

@rowanmiller this seems pretty serious. Data annotations will not be discovered at design time, even though they will work at runtime.

Although I'm not sure what the solution is, here is the problem:

UWP apps at runtime execute on .NET Core (which uses System.ComponentModel.Annotations), but at design time we use the .NET Framework (System.ComponentModel.DataAnnotations). These assemblies are slightly different. This means at design-time, typeof(KeyAttribute) != typeof(KeyAttribute).

To see this more clearly, run this code at design-time. You'll see the conventions don't work because they are looking for the wrong data annotations types.

var output = "";
var keyAttrConventionTypeArg =
    typeof(KeyAttributeConvention).GetTypeInfo().BaseType.GetGenericArguments()[0];

output += "typeof(TAttribute) in KeyAttributeConvention:\n    " + keyAttrConventionTypeArg.AssemblyQualifiedName;

var keyAttrType = typeof(Blog).GetProperty(nameof(Blog.BlogID))
    .GetCustomAttributes(true)
    .Single(a => a.GetType().Name.Contains("KeyAttribute"))
    .GetType();
output += "\ntypeof(KeyAttribute) on Blog.BlogID:\n    " + keyAttrType.AssemblyQualifiedName;

output += "\nAre they equal? " + keyAttrType.Equals(keyAttrConventionTypeArg);
throw new Exception(output);

This outputs:

typeof(TAttribute) in KeyAttributeConvention:
    System.ComponentModel.DataAnnotations.KeyAttribute, System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
typeof(KeyAttribute) on Blog.BlogID:
    System.ComponentModel.DataAnnotations.KeyAttribute, System.ComponentModel.Annotations, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Are they equal? False

@natemcmaster natemcmaster changed the title from DataAnnotations not working in UWP to DataAnnotations not working in UWP at design-time Jul 6, 2016

@ajcvickers

This comment has been minimized.

Show comment
Hide comment
@ajcvickers

ajcvickers Jul 6, 2016

Member

This:

UWP apps at runtime execute on .NET Core (which uses System.ComponentModel.Annotations), but at design time we use the .NET Framework (System.ComponentModel.DataAnnotations). These assemblies are slightly different. This means at design-time, typeof(KeyAttribute) != typeof(KeyAttribute).

seems to indicate that unification is not happening correctly. They should resolve to exactly the same thing.

Member

ajcvickers commented Jul 6, 2016

This:

UWP apps at runtime execute on .NET Core (which uses System.ComponentModel.Annotations), but at design time we use the .NET Framework (System.ComponentModel.DataAnnotations). These assemblies are slightly different. This means at design-time, typeof(KeyAttribute) != typeof(KeyAttribute).

seems to indicate that unification is not happening correctly. They should resolve to exactly the same thing.

@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Jul 6, 2016

Member

Ideally, we could solve this with assembly redirects. But unless there is some xml magic I'm not familiar with, I don't think we can redirect to a different public key token and assembly name.

Two other ideas to solve this:

  1. Change PropertyAttributeConvention<TAttribute> to use string comparisons on TAttribute instead of .GetCustomAttribute<TAttribute>(). src
  2. Host UWP design time in the UWP app itself instead of a desktop .NET. (This would also resolve #5471). As UWP is sandboxed, it doesn't have access to the filesystem so we would probably follow how UWP test runners execute: create a network-based protocol and launching UWP with special flags to use a socket for design-time commands/responses.
Member

natemcmaster commented Jul 6, 2016

Ideally, we could solve this with assembly redirects. But unless there is some xml magic I'm not familiar with, I don't think we can redirect to a different public key token and assembly name.

Two other ideas to solve this:

  1. Change PropertyAttributeConvention<TAttribute> to use string comparisons on TAttribute instead of .GetCustomAttribute<TAttribute>(). src
  2. Host UWP design time in the UWP app itself instead of a desktop .NET. (This would also resolve #5471). As UWP is sandboxed, it doesn't have access to the filesystem so we would probably follow how UWP test runners execute: create a network-based protocol and launching UWP with special flags to use a socket for design-time commands/responses.
@ajcvickers

This comment has been minimized.

Show comment
Hide comment
@ajcvickers

ajcvickers Jul 6, 2016

Member

@natemcmaster When we did the original work on the data annotations package it was supposed to unify for exactly the reasons here. I guess that must have changed. This is disappointing. I wonder if @bricelam has more context on this?

Member

ajcvickers commented Jul 6, 2016

@natemcmaster When we did the original work on the data annotations package it was supposed to unify for exactly the reasons here. I guess that must have changed. This is disappointing. I wonder if @bricelam has more context on this?

@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Jul 6, 2016

Member

The previous version of System.ComponentModel.Annotations works. Users can workaround this by forcing a downgrade. Add this to project.json:

"dependencies": {
    "System.ComponentModel.Annotations": "4.0.10"
}
Member

natemcmaster commented Jul 6, 2016

The previous version of System.ComponentModel.Annotations works. Users can workaround this by forcing a downgrade. Add this to project.json:

"dependencies": {
    "System.ComponentModel.Annotations": "4.0.10"
}
@minimalisticMe

This comment has been minimized.

Show comment
Hide comment
@minimalisticMe

minimalisticMe Jul 7, 2016

@natemcmaster I cannot confirm your workaround. My project.json now looks like this:

{
  "dependencies": {
    "LiveSDK": "5.6.3",
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0",
    "Microsoft.OneDriveSDK": "1.1.47",
    "System.ComponentModel.Annotations": "4.0.10"
  },
  "frameworks": {
    "uap10.0": {}
  },
  "runtimes": {
    "win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}
  }
}

and doing a Add-Migration now gives the following Error message:

`Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

minimalisticMe commented Jul 7, 2016

@natemcmaster I cannot confirm your workaround. My project.json now looks like this:

{
  "dependencies": {
    "LiveSDK": "5.6.3",
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0",
    "Microsoft.OneDriveSDK": "1.1.47",
    "System.ComponentModel.Annotations": "4.0.10"
  },
  "frameworks": {
    "uap10.0": {}
  },
  "runtimes": {
    "win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}
  }
}

and doing a Add-Migration now gives the following Error message:

`Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Jul 7, 2016

Member

@minimalisticMe my guess is something in your other dependencies expects 4.1.0. This is my project.json and it works without redirects:

  "dependencies": {
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0",
    "System.ComponentModel.Annotations": "4.0.10"
  },

You can add a binding redirect manually to workaround this (issue #5471).

Member

natemcmaster commented Jul 7, 2016

@minimalisticMe my guess is something in your other dependencies expects 4.1.0. This is my project.json and it works without redirects:

  "dependencies": {
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0",
    "System.ComponentModel.Annotations": "4.0.10"
  },

You can add a binding redirect manually to workaround this (issue #5471).

@minimalisticMe

This comment has been minimized.

Show comment
Hide comment
@minimalisticMe

minimalisticMe Jul 8, 2016

@natemcmaster you are right, it is working on a minimal example. Now I'll check up my code, where I went wrong.

minimalisticMe commented Jul 8, 2016

@natemcmaster you are right, it is working on a minimal example. Now I'll check up my code, where I went wrong.

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 20, 2016

@rowanmiller My understanding is the fluent api does not support check constraints (at least against SQLite). Data validation becomes problematic if data annotations don't work with EF commands, does it not? Or have I missed something?

VagueGit commented Jul 20, 2016

@rowanmiller My understanding is the fluent api does not support check constraints (at least against SQLite). Data validation becomes problematic if data annotations don't work with EF commands, does it not? Or have I missed something?

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 20, 2016

Member

@VagueGit the Fluent API allows you to do everything that can be done with data annotations. Could you share the specific example of the annotation you aren't sure how to express in the Fluent API?

Member

rowanmiller commented Jul 20, 2016

@VagueGit the Fluent API allows you to do everything that can be done with data annotations. Could you share the specific example of the annotation you aren't sure how to express in the Fluent API?

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 20, 2016

Thank you for the prompt response @rowanmiller. As a Database Dude my preference is to apply check constraints to the database. I considered data annotations when it appeared EF does not support check constraints against SQLite.

As an example, we need to constrain the values of an integer column to 0-5. The data annotation equivalent is [Range(0,5)]. EF ignores this in UWP at design time so being able to express constraints in the fluent API becomes more important.

VagueGit commented Jul 20, 2016

Thank you for the prompt response @rowanmiller. As a Database Dude my preference is to apply check constraints to the database. I considered data annotations when it appeared EF does not support check constraints against SQLite.

As an example, we need to constrain the values of an integer column to 0-5. The data annotation equivalent is [Range(0,5)]. EF ignores this in UWP at design time so being able to express constraints in the fluent API becomes more important.

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 21, 2016

Member

@VagueGit EF doesn't do anything with the [Range] attribute, so the EF model should be unaffected by this attribute being present or not.

Member

rowanmiller commented Jul 21, 2016

@VagueGit EF doesn't do anything with the [Range] attribute, so the EF model should be unaffected by this attribute being present or not.

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 21, 2016

@rowanmiller but wouldn't it be nice if it did especially as UWP ignores data annotations at design time? I don't think we are alone in trying to migrate to EF from systems with lots of validation on the database. In trying to migrate to EF we seem to be giving up the ability to validate data on the database. It would be great if we could have something in OnModelCreating along the lines of .ForSQLiteCheckConstraint and we pass in some SQL eg "(Condition1 AND Condition2) OR Condition3 OR (Condition4 AND Condition5 OR Condition6)". All EF would have to do is add WITH CHECK and append the SQL.

VagueGit commented Jul 21, 2016

@rowanmiller but wouldn't it be nice if it did especially as UWP ignores data annotations at design time? I don't think we are alone in trying to migrate to EF from systems with lots of validation on the database. In trying to migrate to EF we seem to be giving up the ability to validate data on the database. It would be great if we could have something in OnModelCreating along the lines of .ForSQLiteCheckConstraint and we pass in some SQL eg "(Condition1 AND Condition2) OR Condition3 OR (Condition4 AND Condition5 OR Condition6)". All EF would have to do is add WITH CHECK and append the SQL.

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 21, 2016

Member

As far as I know UWP doesn't ignore annotations at runtime. The issue being discussed here is that they are opaque to our design-time tooling because we load up the assembly in the Full .NET Framework after it was compiled against .NET Core. Agreed that translating [Range] into a CHECK constraint is a possibility - feel free to open a new issue for that feature request. You can also do this manually by using the SQL(...) method in migrations.

Member

rowanmiller commented Jul 21, 2016

As far as I know UWP doesn't ignore annotations at runtime. The issue being discussed here is that they are opaque to our design-time tooling because we load up the assembly in the Full .NET Framework after it was compiled against .NET Core. Agreed that translating [Range] into a CHECK constraint is a possibility - feel free to open a new issue for that feature request. You can also do this manually by using the SQL(...) method in migrations.

@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Jul 21, 2016

Member

I suggested earlier modifying project.json to use a downgraded version of System.ComponentModel.Annotations, but after playing with this, there is a better workaround.

Add this binding redirect so that tooling unifies types correctly:

      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations"
                          publicKeyToken="b03f5f7f11d50a3a"
                          culture="neutral" />
        <bindingRedirect oldVersion="4.1.0.0"
                         newVersion="4.0.0.0"/>
      </dependentAssembly>

Hopefully we can automate this with #5471

Member

natemcmaster commented Jul 21, 2016

I suggested earlier modifying project.json to use a downgraded version of System.ComponentModel.Annotations, but after playing with this, there is a better workaround.

Add this binding redirect so that tooling unifies types correctly:

      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations"
                          publicKeyToken="b03f5f7f11d50a3a"
                          culture="neutral" />
        <bindingRedirect oldVersion="4.1.0.0"
                         newVersion="4.0.0.0"/>
      </dependentAssembly>

Hopefully we can automate this with #5471

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 21, 2016

Apologies @rowanmiller I meant 'UWP ignores data annotations at design time'. I have edited my comment. What do you mean by 'You can also do this manually by using the SQL(...)' Can you point me toward some guidance please?

VagueGit commented Jul 21, 2016

Apologies @rowanmiller I meant 'UWP ignores data annotations at design time'. I have edited my comment. What do you mean by 'You can also do this manually by using the SQL(...)' Can you point me toward some guidance please?

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 21, 2016

Member

@VagueGit I guess what I meant is that the fact that EF Core doesn't pickup the [Range] attribute when it's creating migrations etc. should have zero impact on the application since that attribute doesn't have any impact on the EF model anyways. Even if we did pick it up, the result of the command would be the same.

When you are using migrations, you can edit a scaffolded migration to use the SQL(..) API to run any SQL command against the database. You could use this to issue the command to execute the CHECK constraint you want. Obviously this isn't happening automatically based on the attribute, but it allows you to get the schema you want.

Member

rowanmiller commented Jul 21, 2016

@VagueGit I guess what I meant is that the fact that EF Core doesn't pickup the [Range] attribute when it's creating migrations etc. should have zero impact on the application since that attribute doesn't have any impact on the EF model anyways. Even if we did pick it up, the result of the command would be the same.

When you are using migrations, you can edit a scaffolded migration to use the SQL(..) API to run any SQL command against the database. You could use this to issue the command to execute the CHECK constraint you want. Obviously this isn't happening automatically based on the attribute, but it allows you to get the schema you want.

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 21, 2016

@rowanmiller editing the migration doesn't quite solve the problem ... even though I do miss writing SQL :-) During development the migration is repeatedly being removed and recreated. It would be better if OnModelCreating gave us some method to have custom SQL applied to table creation; entity.HasSQL("..."), or entity.Property(...).HasSQL("...") could be a catch all for any functionality that ef does not yet support and allow us to apply whatever tweaks to the design that isn't ready available otherwise. Those tweaks would then apply to the scaffolded migration without the migration having to be edited repeatedly.

VagueGit commented Jul 21, 2016

@rowanmiller editing the migration doesn't quite solve the problem ... even though I do miss writing SQL :-) During development the migration is repeatedly being removed and recreated. It would be better if OnModelCreating gave us some method to have custom SQL applied to table creation; entity.HasSQL("..."), or entity.Property(...).HasSQL("...") could be a catch all for any functionality that ef does not yet support and allow us to apply whatever tweaks to the design that isn't ready available otherwise. Those tweaks would then apply to the scaffolded migration without the migration having to be edited repeatedly.

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 29, 2016

Member

Moving out of 1.0.1, but @natemcmaster can you prioritize getting an issue handed off to DataAnnotations folks about unification not working (@divega can provide more context if needed).

Member

rowanmiller commented Jul 29, 2016

Moving out of 1.0.1, but @natemcmaster can you prioritize getting an issue handed off to DataAnnotations folks about unification not working (@divega can provide more context if needed).

@rowanmiller rowanmiller modified the milestones: 1.1.0, 1.0.1 Jul 29, 2016

@VagueGit

This comment has been minimized.

Show comment
Hide comment
@VagueGit

VagueGit Jul 29, 2016

@rowanmiller > the fact that EF Core doesn't pickup the [Range] attribute when it's creating migrations etc. should have zero impact on the application since that attribute doesn't have any impact on the EF model anyways

Does this mean we cannot look forward to EF applying data annotations as check constraints on a table design?

VagueGit commented Jul 29, 2016

@rowanmiller > the fact that EF Core doesn't pickup the [Range] attribute when it's creating migrations etc. should have zero impact on the application since that attribute doesn't have any impact on the EF model anyways

Does this mean we cannot look forward to EF applying data annotations as check constraints on a table design?

@rowanmiller

This comment has been minimized.

Show comment
Hide comment
@rowanmiller

rowanmiller Jul 29, 2016

Member

@VagueGit it's a reasonable feature request, feel free to open a new issue for it. It would be a lower priority than a number of other things on our backlog though. It is unrelated to the issue being tracked here though... so any further discussion should occur on a new issue.

Member

rowanmiller commented Jul 29, 2016

@VagueGit it's a reasonable feature request, feel free to open a new issue for it. It would be a lower priority than a number of other things on our backlog though. It is unrelated to the issue being tracked here though... so any further discussion should occur on a new issue.

@divega

This comment has been minimized.

Show comment
Hide comment
@divega

divega Jul 30, 2016

Member

Re the main issue here, @natemcmaster already helped me understand why this actually is a design-time issue in our tooling and not a unification issue with UWP. We should look into programmatically applying the binding redirect or at a long term solution like #6128.

Member

divega commented Jul 30, 2016

Re the main issue here, @natemcmaster already helped me understand why this actually is a design-time issue in our tooling and not a unification issue with UWP. We should look into programmatically applying the binding redirect or at a long term solution like #6128.

@ole1986

This comment has been minimized.

Show comment
Hide comment
@ole1986

ole1986 Aug 1, 2016

Guess I have a simliar issue when trying to add ApplicationUser (inherited from IdentityUser) as foreignKey from another Model...

Example:

public class Demo
    {
        [Key]
        public int ID { get; set; }

        public Guid UserID { get; set; }

        [ForeignKey("UserID")]
        public virtual ApplicationUser User { get; set; }
    }

This causes the Add-Migration command to output:

The key {'UserID'} contains properties in shadow state and is referenced by a relationship from 'ApplicationUser' to 'Demo.User'. Configure a non-shadow principal key for this relationship

when using

Microsoft.EntityFrameworkCore.*": "1.0.0-rc2-final"

ole1986 commented Aug 1, 2016

Guess I have a simliar issue when trying to add ApplicationUser (inherited from IdentityUser) as foreignKey from another Model...

Example:

public class Demo
    {
        [Key]
        public int ID { get; set; }

        public Guid UserID { get; set; }

        [ForeignKey("UserID")]
        public virtual ApplicationUser User { get; set; }
    }

This causes the Add-Migration command to output:

The key {'UserID'} contains properties in shadow state and is referenced by a relationship from 'ApplicationUser' to 'Demo.User'. Configure a non-shadow principal key for this relationship

when using

Microsoft.EntityFrameworkCore.*": "1.0.0-rc2-final"

@ole1986

This comment has been minimized.

Show comment
Hide comment
@ole1986

ole1986 Aug 2, 2016

Workaround is to change the datatype from Guid to string in property UserID

public class Demo
    {
        [Key]
        public int ID { get; set; }

        public string UserID { get; set; }

        [ForeignKey("UserID")]
        public virtual ApplicationUser User { get; set; }
    }

After this the lambda .Include() is working as expected:
var listOfDemoWithUser = new ApplicationDbContext().Demo.Include(l => l.User).ToList();

ole1986 commented Aug 2, 2016

Workaround is to change the datatype from Guid to string in property UserID

public class Demo
    {
        [Key]
        public int ID { get; set; }

        public string UserID { get; set; }

        [ForeignKey("UserID")]
        public virtual ApplicationUser User { get; set; }
    }

After this the lambda .Include() is working as expected:
var listOfDemoWithUser = new ApplicationDbContext().Demo.Include(l => l.User).ToList();

@smitpatel

This comment has been minimized.

Show comment
Hide comment
@smitpatel

smitpatel Aug 2, 2016

Contributor

@ole1986 - In the default ApplicationUser the PK is of string type so when creating the relationship targeting ApplicationUser as above, EF needs the foreign key property to be of type string. The ForeignKeyAttribute tells EF to use UserID as FK property. Now if UserID is of string type all works fine as you showed in your work-around. But as per the first model UserID is of type Guid. Since user specified it using annotation, EF will use it as foreign key property. But Guid type FK property needs to target Guid Principal key. Since PK of ApplicationUser is different type, EF will create shadow principal key temporarily while building model so that user can replace it using fluent API. If that is not done the exception as above is thrown during validation.

Contributor

smitpatel commented Aug 2, 2016

@ole1986 - In the default ApplicationUser the PK is of string type so when creating the relationship targeting ApplicationUser as above, EF needs the foreign key property to be of type string. The ForeignKeyAttribute tells EF to use UserID as FK property. Now if UserID is of string type all works fine as you showed in your work-around. But as per the first model UserID is of type Guid. Since user specified it using annotation, EF will use it as foreign key property. But Guid type FK property needs to target Guid Principal key. Since PK of ApplicationUser is different type, EF will create shadow principal key temporarily while building model so that user can replace it using fluent API. If that is not done the exception as above is thrown during validation.

@natemcmaster

This comment has been minimized.

Show comment
Hide comment
@natemcmaster

natemcmaster Aug 18, 2016

Member

I've investigated more. This issue boils down the same bug covered by #5471. We talked to the .NET team to confirm this is not a bug in .NET itself, but rather in the way we use it at design-time. There are two solutions:

(1) manually use a binding redirect see #5945 (comment). This manually effort can be automated when we solve #5471.

(2) Use .NET Core for UWP design-time operations instead .NET Framework process. #6128 proposes a way to do this, but as this will take considerable effort, we'll get faster results that should be sufficient to solve the DataAnnotations bug by focusing effort on #5471.

Closing: ongoing work is already tracked in #5471 and #6128.

Member

natemcmaster commented Aug 18, 2016

I've investigated more. This issue boils down the same bug covered by #5471. We talked to the .NET team to confirm this is not a bug in .NET itself, but rather in the way we use it at design-time. There are two solutions:

(1) manually use a binding redirect see #5945 (comment). This manually effort can be automated when we solve #5471.

(2) Use .NET Core for UWP design-time operations instead .NET Framework process. #6128 proposes a way to do this, but as this will take considerable effort, we'll get faster results that should be sufficient to solve the DataAnnotations bug by focusing effort on #5471.

Closing: ongoing work is already tracked in #5471 and #6128.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment