1

In my web project I am trying to use Entity Framework 6 model first approach. Model is generated from existing database in separate project. For model generation connection string is saved in app.config of data access project. In main web project (ASP.NET Core MVC) I am trying to inject context creation in Startup.cs class.

Below is a code for context partial class. Partial class is used because context is auto generated from template.

[DbConfigurationType(typeof(EntityFrameworkConfig))]
public partial class MyEntities
{
    public MyEntities(String name) : base(name)
    {    
    }
}

For using entity framework configuration from code instead app.config that cannot be used with asp.net core projects I have config class inherited from System.Data.Entity.DbConfiguration

public class EntityFrameworkConfig : DbConfiguration
{
    public EntityFrameworkConfig()
    {
         this.SetDefaultConnectionFactory(new SqlConnectionFactory());
         this.SetProviderServices(SqlProviderServices.ProviderInvariantName,       SqlProviderServices.Instance);
    }
}

In config.json in web project i have connection string:

{
    "MailSettings": {
    "ToAddress": "foo@bar.com"
    },
    "Connection": {
    "ConnectionString": "data source=SERVER;initial catalog=DB;user id=user;password=pwd;MultipleActiveResultSets=True;App=EntityFramework;"
    }
}

In Startup.cs :

public void ConfigureServices(IServiceCollection services)
{
    ...

    String connStr = _config["Connection:ConnectionString"];
    services.AddScoped((_) => new MyEntities(connStr));
    ...

}

I am experiencing UnintentionalCodeFirstException thrown from OnModelCreating event of auto generated context class:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    throw new UnintentionalCodeFirstException();
}

Is this proper way to use Entity framework 6 model first approach with asp.net Core MVC project and what is the reason for this exception ?

Tseng
  • 61,549
  • 15
  • 193
  • 205
srlle
  • 51
  • 1
  • 7

2 Answers2

2

These are the steps you should follow when you're working with the ASP.net Core and EF 6.This is a just an example.You have to change it according to your use case.

Step 1 : project.json

Specify a single target for the full .NET Framework:

"frameworks": {
    "net46": {}
}

Step 2 : Setup connection strings and dependency injection

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
    {
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped((_) => new ApplicationDbContext(Configuration["Data:DefaultConnection:ConnectionString"]));

    // Configure remaining services
}

Step 3 : Migrate configuration from config to code

Entity Framework 6 allows configuration to be specified in xml (in web.config or app.config) or through code. As of ASP.NET Core, all configuration is code-based.

<entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
        <parameters>
            <parameter value="mssqllocaldb" />
        </parameters>
    </defaultConnectionFactory>
    <providers>
        <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
</entityFramework>

Then :

[DbConfigurationType(typeof(CodeConfig))] // point to the class that inherit from DbConfiguration
public class ApplicationDbContext : DbContext
{
    [...]
}

public class CodeConfig : DbConfiguration
{
    public CodeConfig()
    {
        SetProviderServices("System.Data.SqlClient",
            System.Data.Entity.SqlServer.SqlProviderServices.Instance);
    }
}

Reference : ASP.NET Core and Entity Framework 6

Sampath
  • 63,341
  • 64
  • 307
  • 441
  • I did exactly the same but always getting that exception. Only difference is that I've used partial class to extend auto generated context class with new constructor to enable passing connection string as argument, and in my project.json I have net462 as framework. – srlle Sep 13 '16 at 08:25
  • have you defined ```` correctly ? can you show it ? – Sampath Sep 13 '16 at 09:55
  • This is what I've got in web.config, and I think it is same configuration as you suggested. – srlle Sep 13 '16 at 10:56
  • can you set the `connection string` as shown like my example and let me know ? – Sampath Sep 13 '16 at 21:06
  • I am setting connection string in same way as in your code, difference is that in your code context class is not defined as partial. I must define context class as partial in order to use model generated from existing database. In debug mode I can see that context has connection string from my config.json provided but when trying execute first query on context (read any entity) UnintentionalCodeFirstException exception is thrown. – srlle Sep 14 '16 at 09:27
0

After digging I found that solution is to provide connection string in entity framework format that enables DbContext class to load model metadata.
In Startup.cs:

String connStr = _config["Connection:ConnectionString"];
String efConnString = BuildConnectionString(connStr);
services.AddScoped((_) => new MyEntities(efConnString));  
....  
....
private String BuildConnectionString(String cs)
{
    EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
    entityBuilder.Provider = "System.Data.SqlClient";
    entityBuilder.ProviderConnectionString = cs;
    entityBuilder.Metadata = @"res://*/XXXWebModel.csdl|
                        res://*/XXXWebModel.ssdl|
                        res://*/XXXWebModel.msl";
    return entityBuilder.ToString();
}

When connection string is provided in format that doesn't contain metadata information where to look for generated model entity framework finds that code first approach is used and that is why method OnModelCreating is called that throws exception :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    throw new UnintentionalCodeFirstException();
}  

Sending connection string formated as entity framework connection string that contains metadata information solves problem.

srlle
  • 51
  • 1
  • 7