This project has moved. For the latest updates, please go here.

How does it support IdentityDbContext<TUser>

Nov 19, 2013 at 11:57 AM
Edited Nov 19, 2013 at 9:59 PM
Since IdentityDbContext<TUser> hides the constructor of passing in DBConnection. How should I use Effort to pass in the fake one?

I suppose I can always create a fake DBContext for testing purpose.

Thanks

Awesome project!

ay
Coordinator
Nov 20, 2013 at 10:24 AM
Hello,

In which library can I find IdentityDbContext? It does not seem to be part of EF.
Dec 22, 2013 at 9:05 AM
It's part of Microsoft.AspNet.Identity.EntityFramework, as part of their new ASP.NET Identity stuff.

I'm having the same issue. Ironically one of the goals of ASP.NET Identity is to make things easier to unit test... don't think that's gone too well for them.
Jan 14, 2014 at 7:14 PM
+1

Please answer
Jan 20, 2014 at 10:15 PM
Hello, I have the same problem. Here is some info about my code:

I made AppContext class that extends IdentitDbContext<User>. User is my class that extends ApplicationUser class.
public class AppContext : IdentityDbContext<User>
    {
        public AppContext()
            : base("DefaultConnection")
        {
        }

        public AppContext(DbConnection connection) 
            : base(connection.ConnectionString)
        {
        }
}
In the code you can see that i have constructor that receives DbConnection object and passes ConnectionString to the base constructor.

So, I try to init my context like this:
    // creating connection
    DbConnection connection = Effort.DbConnectionFactory.CreateTransient();

    // creating the app context
    AppContext context = new AppContext(connection);

    // trying to save user (user is made earlier in code)
    context.Users.Add(user);
On that last line it gives me exception:
System.ArgumentException: System.ArgumentException: Keyword not supported: 'instanceid'.
Coordinator
Jan 26, 2014 at 2:30 AM
Edited Jan 26, 2014 at 2:38 AM
I came with the following solution. Unfortunately you cannot use transient connections this way.

App.config
<connectionStrings>
    <add name="fakeIdentity" connectionString="" />
  </connectionStrings>
Program.cs
class Program
{
    static void Main(string[] args)
    {
        Effort.Provider.EffortProviderConfiguration.RegisterProvider();

        var builder = new Effort.Provider.EffortConnectionStringBuilder()
        {
            InstanceId = "userstore",
            IsTransient = false,
            DataLoaderType = typeof(CsvDataLoader),
            // Sample path for csv files containing the users
            DataLoaderArgument =
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "IdentityData")
        };

        // HACK: make the connection string configuration editable
        // This is only for demonstration, you can put the value of 
        // "builder.ConnectionString" manualy to the app.config/web.config 
        // file
        var settings = ConfigurationManager.ConnectionStrings["fakeIdentity"];
        var fi = typeof(ConfigurationElement).GetField(
           "_bReadOnly", 
            BindingFlags.Instance | BindingFlags.NonPublic);
        fi.SetValue(settings, false);

        // Provider name is required to make EF use Effort
        settings.ProviderName = "Effort.Provider";
        // Connection string constructed my the builder
        settings.ConnectionString = builder.ConnectionString;

        AppContext context = new AppContext("fakeIdentity");

        // This worked for me
        var users = context.Users.ToList();
    }
}

public class AppContext : IdentityDbContext<User>
{
    public AppContext(string connectionName)
        : base(connectionName)
    {
    }
}
Coordinator
Jan 26, 2014 at 2:36 AM
You may want to vote for the following feature request:
https://aspnetidentity.codeplex.com/workitem/1991
May 16, 2014 at 3:12 PM
Jumping on an old thread, but I'm currently looking to tackle this same issue of using Effort with a context inheriting from IdentityDbContext<TUser>.

I can see following your feature request a new constructor has been added, so I now have this:
public class ApplicationDbContext : IdentityDbContext<User>
{
    public ApplicationDbContext(DbConnection connection)
        : base(connection, CreateDbCompiledModel(connection), true)
    {
        InitializeContext();
    }

    private static DbCompiledModel CreateDbCompiledModel(DbConnection connection)
    {
        var builder = new DbModelBuilder();
        var model = builder.Build(connection);
        return model.Compile();
    }
}

And a test with some setup code like this:
        var loader = new Effort.DataLoaders.CsvDataLoader(dataFolder);
        var connection = Effort.DbConnectionFactory.CreateTransient(loader);
        var ctx = new ApplicationDbContext(connection);
        ...
However when I call into some code referencing this context I get an error of "The entity type XXX is not part of the model for the current context." I have an appropriate CSV file in place for the "table" I'm attempting to read from.

Appreciate some guidance on if I'm going about this the right way.

Thanks

Andy
Coordinator
May 25, 2014 at 10:49 AM
Edited May 26, 2014 at 9:55 AM
Hello,

Wow, they did not include the right constructor. Maybe someone should write another ticket for them.

The following workaround seems to work:
var conn = Effort.DbConnectionFactory.CreateTransient();
var dummyContext = new IdentityDbContext();

var builder = new DbModelBuilder();

var m = typeof(IdentityDbContext).GetMethod("OnModelCreating", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(dummyContext, new object[] { builder });

var model = builder.Build(conn).Compile();

var context = new IdentityDbContext(conn, model, false);
May 26, 2014 at 12:28 PM
Thanks Tamas. That looks to have got me bit further. I'm now getting the following exception on the var model = builder.Build(conn).Compile(); line though:
System.InvalidOperationException: Sequence contains no matching element
Result StackTrace:  
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
   at System.Data.Entity.Utilities.DbProviderManifestExtensions.GetStoreTypeFromName(DbProviderManifest providerManifest, String name)
   at System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.Configure(EdmProperty column, EntityType table, DbProviderManifest providerManifest, Boolean allowOverride, Boolean fillFromExistingConfiguration)
   at System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.<>c__DisplayClass1.<Configure>b__0(Tuple`2 pm)
   at System.Data.Entity.Utilities.IEnumerableExtensions.Each[T](IEnumerable`1 ts, Action`1 action)
   at System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration.Configure(IEnumerable`1 propertyMappings, DbProviderManifest providerManifest, Boolean allowOverride, Boolean fillFromExistingConfiguration)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.StructuralTypeConfiguration.ConfigurePropertyMappings(IList`1 propertyMappings, DbProviderManifest providerManifest, Boolean allowOverride)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigurePropertyMappings(DbDatabaseMapping databaseMapping, EntityType entityType, DbProviderManifest providerManifest, Boolean allowOverride)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(EntityType entityType, DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
   at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntityTypes(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
   at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
Reading around suggests this can be an obscure message indicating a column type configuration is incorrect, and I've seen some reference to it on your other issues raised. But not clear if there's a solution you have for this so again would appreciate any advice. I'm using date, string and numeric columns.

Thanks

Andy
Coordinator
May 26, 2014 at 12:44 PM
Edited May 26, 2014 at 12:45 PM
Yes, you should not set the sql server column types explicitly, because the Effort provider does not know them. Try to use generic fluid API calls to achieve the desired type. For example the following code creates an nvarchar(100) column:
builder
  .Entity<Entity>()
  .Property(x => x.Property)
  .IsUnicode(true)
  .IsVariableLength()
  .HasMaxLength(100);
May 27, 2014 at 9:19 PM
I expect then it's mostly where I'm setting some dates as "smalldatetime". Can you do that using the method you've suggested?
Coordinator
May 27, 2014 at 11:39 PM
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   // Unfortunately this does not work
   modelBuilder.Entity<Foo>().Property(x => x.Date).HasPrecision(0);

   // Set only for SQL Server
   if (this.Database.Connection is SqlConnection)
      modelBuilder.Entity<Foo>().Property(x => x.Date).HasColumnType("smalldatetime");    
   
   base.OnModelCreating(modelBuilder);
 }
May 29, 2014 at 9:14 AM
Thanks again Tamas

Unfortunately this suggestion didn't quite work, as this.Database.Connection is SqlConnection returned true in the run through OnModelCreating when effort was instantiating. But with a small tweak it did.

I just made another method on my context class:
protected void ConfigureModel(DbModelBuilder modelBuilder, bool doSqlSpecificConfigurations)
{
    ....
    if (doSqlSpecificConfigurations)
    {
        modelBuilder.Entity<Activity>().Property(x => x.StartDateAndTime).HasColumnType("smalldatetime");
    }
    ....
}
And called that from the test set up code, with the second parameter set to false. For the normal application set up, I call it from the OnModelCreating event handler with the flag set to true.

Cheers

Andy
Sep 12, 2014 at 4:57 AM
Edited Sep 12, 2014 at 4:59 AM
Hi,

I too am having issues with the IdentityDbContext.

The following code:
    ApplicationDbContext context;

[SetUp]
public void Init()
{
    var conn = DbConnectionFactory.CreateTransient();
    var dummyContext = new ApplicationDbContext();
    var builder = new DbModelBuilder();
    var m = typeof(ApplicationDbContext).GetMethod("OnModelCreating", BindingFlags.NonPublic | BindingFlags.Instance);
    m.Invoke(dummyContext, new object[] { builder });

    var model = builder.Build(conn).Compile();

    context = new ApplicationDbContext(conn, model, false);
}

[Test]
public void AddsAnItem()
{
    context.CompletedActivities.Add(new CompletedActivity());
    Assert.AreEqual(1, context.CompletedActivities.Count());
}
My context looks like:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<CompletedActivity> CompletedActivities { get; set; }

    ...

    public ApplicationDbContext() 
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public ApplicationDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)
        : base(existingConnection, model, contextOwnsConnection)
    {
    }

    public static ApplicationDbContext Create()
    {
        Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
        return new ApplicationDbContext();
    }
}
Throws the following error:

Result Message: System.InvalidOperationException : The entity type CompletedActivity is not part of the model for the current context.
Result StackTrace:
at System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType)
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet
1.get_InternalContext()
at System.Data.Entity.Internal.Linq.InternalSet1.ActOnSet(Action action, EntityState newState, Object entity, String methodName)
at System.Data.Entity.Internal.Linq.InternalSet
1.Add(Object entity)
at System.Data.Entity.DbSet`1.Add(TEntity entity)
at ...

The packages I am using are:

<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Effort.EF6" version="1.1.0" targetFramework="net45" />
<package id="EntityFramework" version="6.1.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.EntityFramework" version="2.1.0" targetFramework="net45" />
<package id="Ninject" version="3.2.2.0" targetFramework="net45" />
<package id="NMemory" version="1.0.0" targetFramework="net45" />
<package id="NSubstitute" version="1.7.2.0" targetFramework="net45" />
<package id="NUnit" version="2.6.3" targetFramework="net45" />
</packages>

Any ideas? I would love to use this brilliant looking library for my tests!
Sep 12, 2014 at 9:50 PM
This may be off base but I found a few instances of that error. It was a little while ago but as I remember the work around I found was to add a statement like this to the OnModelCreating event handler:
modelBuilder.Configurations.Add(new CompletedActivityConfiguration());
With CompletedActivityConfiguration defined as:
public class CompletedActivityConfiguration : EntityTypeConfiguration<CompletedActivity>
{
    public CompletedActivityConfiguration()
    {
        // Nothing to do, but seems we need to have this to ensure effort based unit tests recognise it as part of the context
    }
}
Andy
Sep 12, 2014 at 11:52 PM
Great" That seems to work. Even better you can do this:

builder.Configurations.Add(new EntityTypeConfiguration<CompletedActivity>());

And it still works.

This means you could probably write a helper function that scans the ApplicationDBContext for entity types then adds it to the configuration.
Coordinator
Sep 23, 2014 at 3:29 PM
Edited Sep 23, 2014 at 3:29 PM
You can also initialize the DbModelBuilder like this to include the entity types of the DbSet properties.
DbContext context = ...

var internalProp = typeof(DbContext)
    .GetProperty("InternalContext", BindingFlags.NonPublic | BindingFlags.Instance);

object internalContext = internalProp.GetValue(context);

var method = internalContext.GetType().GetMethod("CreateModelBuilder");

DbModelBuilder builder = (DbModelBuilder)method.Invoke(internalContext, new object[0]);
However I think too many hacks are needed at this point. I will ask the devs of the Identity lib to include a more friendly ctor.
Coordinator
Sep 23, 2014 at 3:37 PM
The issue is reopened.
Feb 18, 2015 at 9:52 AM
It has been closed, available in nightly builds. Hopefully I will have time to try it out this week.