Share via


MVC4 SimpleMembership Model and an Entity Framework Model in the Same Solution

This page is in development, while I try to figure a way through my coding issues. Please do not edit unless you have the solution.

The Problem

I am trying to publish an MVC 4 website, including "out of the box" SimpleMembership.

The problem arises when I simply try to add an existing or new entity model, for use in linq with web services, from the same website.

I have spent considerable time trying to add an existing edmx model to a new MVC site, even patching to EF6, which has a fix for multiple models, which eventually worked fine locally. 

When I published my solution to Azure, it broke with no useful error information.

Initial Setup

Create an empty Azure database instance

Create an empty Azure website and link resources to the database

Create a new MVC4 project in Visual Studio 2012, leave default "Internet Application" template and "Razor" view engine.

Where we begin

Here is the initial collapsed view of the solution, straight out of the box.

Below is the start of Web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework"
             type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, 
             EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
             requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection"
         connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MVC4_EF4-20131210000611;
         Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MVC4_EF4-20131210000611.mdf" 
         providerName="System.Data.SqlClient" />
  </connectionStrings>

Now run this vanilla out-of-the-box project, with absolutely no changes.

It is AccountController.cs that has the [Authorise] and [InitializeSimpleMembership] attributes, so you have to navigate to the login page to trigger the creation of the SimpleMembership schema in the database.

By default, the connection string creates a LocalDb database. and generates the tables and foreign key constraints in this new database.

That should all work fine on your local machine.

Publish to Azure

Log back into Azure, select the new website, go to the "dashboard" tab and "Download the publish profile".

Back in Visual Studio, right-click the website and select "Publish" (or select menu option "Build" / "Publish YourProjName")

Import the publish profile you just downloaded.

Click Next to the "Settings" page and change the "UsersContext" (DefaultConnection) to use the Azure database that was resource linked, above.

Click "Finish" to publish it to Azure.

This should also work fine. Go manage the Azure database and again you'll see the tables added to the empty database and registered users get added.

Now Add EF4

This is where things start to go wrong. 

Firstly, right-click the Models folder, Add / New Item / Data / "ADO.NET Entity Data Model", named whatever you like.

In the Entity Data Model Wizard, select "Generate from database" and on the next page select "DefaultConnection (Settings)"

Uncheck "Save entity connection settings in Web.Config". This prevents a second connection string being used. I'm not sure the relevance of this yet.

Click next, choose your preferred Entity Framework version, click next.

Firstly, if you did not select any objects and click Finish, after a warning, you would get an empty model. Again, this builds, runs locally and when republished to Azure.

However, we will include the five new Simple Membership tables, as shown below...

... then you get the first compilation errors:

"Error 1 Missing partial modifier on declaration of type 'MVC4_EF4.Models.UserProfile'; another partial declaration of this type exists c:\users\pedro\documents\visual studio 2012\Projects\MVC4_EF4\MVC4_EF4\Models\AccountModels.cs"

...and...

"Error 2 Ambiguity between 'MVC4_EF4.Models.UserProfile.UserName' and 'MVC4_EF4.Models.UserProfile.UserName' c:\users\pedro\documents\visual studio 2012\Projects\MVC4_EF4\MVC4_EF4\Controllers\AccountController.cs"

UserProfile and navigation property UserName are defined both by the "Code First" model for Simple Membership, and as a partial declaration by the new "tt" files from the EF "Database First" generated model. Because of the folder we chose for the edmx, they also now clash with the namespace for the Account Model.

Fix - Remove tt files

In the Models folder, as indicated by the error above, you will have noticed that the edmx creation wizard has created a .cs file for each table/class. These are created by the newly created tt template files in your project, shown below:

If you delete "Model1.Context.tt" and "Model1.tt" from your project, and recompile, it should now build without any conflicts.

Without those extra partial classes, the project will also run locally, and when published to Azure.

Move the Namespace

Now, delete Model1.edmx and create a new folder called "EF_Data". 

Repeat the steps above to recreate the edmx and supporting files in this new folder, which will therefore be a separate namespace from the Account Model.

This will now compile without the clashes above, however when you run the project you will now get a new error:

private class  SimpleMembershipInitializer
{
    public SimpleMembershipInitializer()
    {
        Database.SetInitializer<UsersContext>(null);
 
        try
        {
            using (var context = new UsersContext())
            {
                if (!context.Database.Exists())
                {
                    // Create the SimpleMembership database without Entity Framework migration schema
                    ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
                }
            }
 
            WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",  "UserName", autoCreateTables: true);
        }
        catch (Exception ex)
        {
            throw new  InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
        }
    }
}

If you put a breakpoint just before this, the actual exception is as follows:

"Schema specified is not valid. Errors: 
The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'UserProfile'. Previously found CLR type 'MVC4_EF4.EF_Data.UserProfile', newly found CLR type 'MVC4_EF4.Models.UserProfile'."

This seems to be an age old problem with EF.
Here is a forum post discussing it.
It seems to be an issue with CreateObjectSet, which ignores namespaces. There are two solutions suggested, which may help you:

  1. Manually rename one of the two clashing classes, making every class unique.
  2. Separate them in different assemblies

Considering the simple steps I've taken to get to this point, neither of these options seem acceptable. 

Fix - Rename the EF entities

Open the edmx in the Entity Data Model Designer.

Click the table headers and rename the table within the designer, like "UserProfile2", "webpages_Roles2", etc.

This will now work fine, but you have to use the new entity names, which isn't a very clean solution.

Fix - Forget the Membership tables, just give me the rest of the database

In some cases, you don't want these tables in my data model and can leave them isolated to the Membership model.

You can have a separate user lookup table, which had your application specific columns and duplicated any important fields (like UserId and UserName).

Open the Entity Data Model Designer and delete the five Membership tables.

This will now run fine, without the schema errors.

More to come...