Using SimpleMembership with EF model-first - entity-framework

Can SimpleMembership be used with EF model-first? When I try it, I get "Unable to find the requested .NET Framework Data Provider" when I call WebSecurity.InitializeDatabaseConnection.
To put it another way: I can't get the call to WebSecurity.InitializeDatabaseConnection to work when the connection string employs the System.Data.EntityClient provider (as it does when using the model-first paradigm).
To repro the issue, create an MVC 4 app, and replace the code-first UserProfile entity class (which you get for free with the MVC 4 template) with a model-first User class that you have created in the Entity Designer:
Create an MVC 4 app in VS 2012 and add a new, blank Entity Data
Model.
Add a new Entity named User to the model, with fields for Id,
UserName, and FullName. So, at this point, the User data entity is
mapped to a Users table and is accessed via a funky connection
string that employs the System.Data.EntityClient provider.
Verify that the EF can access the User entity. One easy way to do
that is to scaffold out a Users controller based on the User table
and its associated DbContext.
Edit the AccountModels.cs file to remove the UserProfile class and
its associated UsersContext class. Replace the references to the
(now missing) UserProfile and UsersContext classes with references
to your new User class and its associated DbContext class.
Move the call to InitializeDatabaseConnection from the
InitializeSimpleMembershipAttribute filter class to the
Application_Start method in Global.asax.cs. While you're at it,
modify the arguments to use your new User entity's connection
string, table name, and UserId column name.
Delete the (no longer used) InitializeSimpleMembershipAttribute
class and the references to it.
When you run the repro, it will get an Exception at the call to InitializeDatabaseConnection.
Bob

SimpleMembership can work with model first. Here is the solution.
1.InitializeSimpleMembershipAttribute.cs from MVC 4 Internet Application templete should look like this
namespace WebAndAPILayer.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
WebSecurity.InitializeDatabaseConnection("ConnStringForWebSecurity", "UserProfile", "Id", "UserName", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("Something is wrong", ex);
}
}
}
}
}
2.Delete CodeFirst Classes from AcountModel.cs
3.Fix AccountCotroler.cs to work with your Model-first DbContext (ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl) method)
4.Define your "ConnStringForWebSecurity" connection string which is not same as that funky conn string for model-first db access, notice that we use provider System.Data.SqlClient not System.Data.EntityClient
<connectionStrings>
<add name="ModelFirstEntityFramework" connectionString="metadata=res://*/Context.csdl|res://*/Context.ssdl|res://*/Context.msl;provider=System.Data.SqlClient;provider
connection string="data source=.\SQLEXPRESS;Initial
Catalog=aspnet-MVC4;Integrated
Security=SSPI;multipleactiveresultsets=True;App=EntityFramework""
providerName="System.Data.EntityClient" />
<add name="ConnStringForWebSecurity" connectionString="data source=.\SQLEXPRESS;Initial Catalog=aspnet-MVC4;Integrated
Security=SSPI" providerName="System.Data.SqlClient" />
</connectionStrings>

That's a bug in MVC 4. There's a workaround in this blog post.
As an action filter, InitializeSimpleMembershipAttribute hooks into OnActionExecuting to perform the lazy initialization work, but this can be too late in the life cycle. The Authorize attribute will need the providers to be ready earlier if it needs to perform role based access checks (during OnAuthorization). In other words, if the first request to a site hits a controller action like the following:
[Authorize(Roles="Sales")]
.. then you’ll have an exception as the filter checks the user’s role but the providers aren’t initialized.
My recommendation is to remove ISMA from the project, and initialize WebSecurity during the application start event.

1 - You need to enable migrations, prefereably with EntityFramework 5
2 - Move your
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
to your Seed method in your YourMvcApp/Migrations/Configuration.cs class
protected override void Seed(UsersContext context)
{
WebSecurity.InitializeDatabaseConnection(
"DefaultConnection",
"UserProfile",
"UserId",
"UserName", autoCreateTables: true);
if (!Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
if (!WebSecurity.UserExists("lelong37"))
WebSecurity.CreateUserAndAccount(
"lelong37",
"password",
new {Mobile = "+19725000000", IsSmsVerified = false});
if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
}
Now EF5 will be in charge of creating your UserProfile table, after doing so you will call the WebSecurity.InitializeDatabaseConnection to only register SimpleMembershipProvider with the already created UserProfile table (In your case, you can replace the "UserProfile" parameter value with your custom table name), also tellling SimpleMembershipProvider which column is the UserId and UserName. I am also showing an example of how you can add Users, Roles and associating the two in your Seed method with custom UserProfile properties/fields e.g. a user's Mobile (number).
3 - Now when you run update-database from Package Manager Console, EF5 will provision your table with all your custom properties
For additional references please refer to this article with sourcecode:
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/

this problem caused by WebSecurity.InitializeDatabaseConnection can't use connection string with System.Data.EntityClient provider name.
providing dual connection string isn't sound good, so you can generate the connection string for EF model first in the constructor in the partial class.
the code is look like bellow
public partial class MyDataContext
{
private static string GenerateConnectionString(string connectionString)
{
var cs = System.Configuration.ConfigurationManager
.ConnectionStrings[connectionString];
SqlConnectionStringBuilder sb =
new SqlConnectionStringBuilder(cs.ConnectionString);
EntityConnectionStringBuilder builder =
new EntityConnectionStringBuilder();
builder.Provider = cs.ProviderName;
builder.ProviderConnectionString = sb.ConnectionString;
builder.Metadata = "res://*/MyDataContext.csdl|" +
"res://*/MyDataContext.ssdl|res://*/MyDataContext.msl";
return builder.ToString();
}
public MyDataContext(string connectionName) :
base(GenerateConnectionString(connectionName)) { }
}
with this trick you can use single connection string on your web config, but one problem you can't use default constructor on your datacontext, instead you should seed connection string name everywhere when you instantiate the datacontext. but it is not a big problem when you use dependency injection pattern.

I´m not able to work with EF and WebMatrix webSecurity class so to avoid this problem and go ahead:
Change my Ef model first to code first.
Change the connection string to use providerName="System.Data.SqlClient"(removing all the metadata information) or use the EF connection
In my case the model, data and web are different proyects so for me is not an issue to remove this information from the web.config on the web.project.
Nowadays websecuroty.initializedatabase dosen't run with EF connection string.
I wish this helps

Related

Issue with Entity Framework 6 while connecting to SQL Server from Azure function V2

I am trying to use an existing library which is a .net library which uses EF 6.0 to connect to a database. Since Azure Functions does not have an app.config file, I am trying to set the connection string using C# code. But I am getting the following exception while connecting to the DB using my DB context:
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
MyDBContext.partial.cs:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class MyDBContext : DbContext
{
public MyDBContext (string ConnectionString)
: base(ConnectionString)
{
}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
I have a method as following to get the DBContext. This method will be used by the library methods to get the DB context instance.
public MyDBContext GetDB( string metadata, string connectionString )
{
EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
b.Metadata = metadata;
b.ProviderConnectionString = connectionString;
b.Provider = "System.Data.SqlClient";
return new MyDBContext (b.ConnectionString);
}
When I execute a library method to load data from db from an Azure function v2, which internally calls the above method to get DB Context and then connects to actual DB. Here MyDBContext object is getting created, but when it connects to db the following exception occurs.
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
I just worked on this issue but for Azure function V1.
When using EF with Azure function, you can specify connection string in 'local.settings.json' file like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"YourEntities": {
"ConnectionString": "metadata=res://*/EF.yourModel.csdl|res://*/EF.yourModel.ssdl|res://*/EF.yourModel.msl;provider=System.Data.SqlClient;provider connection string='data source=yourServer;initial catalog=yourDB;persist security info=True;user id=yourUserID;password=yourPwd;MultipleActiveResultSets=True;App=EntityFramework'",
"ProviderName": "System.Data.EntityClient"
}
}
}
Please pay attention to 'ProviderName' attribute. Case should be exact as shown above and provider should be 'EntityClient'
Plus 'Provider Connection String' attribute of actual connection string should be in single quote (I am not sure why Microsoft did this but this is how it is supposed to be).
This will help you run your function app locally with EF without any more changes
Now for deployment in Azure.
local.settings.json does not get deployed to cloud. As its name suggests it acts as configuration file for local run.
So you need to set connection string in 'Configuration' of Azure function app on portal.
There you can specify following parameters:
Name - 'YourEntities'
value - Just Connection string part from above json file
Type - 'Custom'
Slot Settings - according to your requirement
Now if you notice there is no way to specify ProviderName here. If you try to run function now you will get error for 'missing provider name'
Here your extended DBConfiguration class comes in handy.
Create your DB configuration class as below and specify provider as EntityType
public class YourDBContextConfig : DbConfiguration
{
public YourDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
You can create this class in same file where you have created partial class for your DBContext
Add following attribute to your Context class:
[DbConfigurationType(typeof(YourDBContextConfig))]
Also make sure your partial context class has constructor that takes connection string as parameter and supply it while initializing context:
string connString =
ConfigurationManager.ConnectionStrings["YourEntities"].ConnectionString;
using (YourEntities db = new YourEntities(connString))
{
}
This will work for deployment.

How to provision an EF database using custom provisioning steps?

We are using EF 6 Code First Migrations in a relatively new project (i.e. there isn't a lot of clutter to contend with). Also, as this is an "enterprise-y" application, we have some specific deployment rules for our target database:
all application level data access must be done through a specific DB user (app-user)
this app-user does not have permissions to create new databases
Therefore, in order to correctly provision a new target database for this application, we need to:
CREATE DATABASE [database_name] CONTAINMENT = PARTIAL
CREATE USER [app-user] WITH PASSWORD=N'p#ssw0rd'
(plus assign specific DB roles to this new user)
I was hoping to carry this out by writing a custom IDatabaseInitializer<TContext>, but it seems that I cannot hook into the database initialization at the correct point.
Conceptually, I want to do this:
have one connection string that is used for read/write access to the DB, using the "controller" app-user user
have a separate connection string that is solely used for provisioning the DB, using more privileged credentials
The code I have tried to use looks a bit like this:
internal class ProvisionThenMigrateInitializer<TContext, TConfiguration>
: MigrateDatabaseToLatestVersion<TContext, TConfiguration>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
private readonly DbMigrationsConfiguration _readWriteConfiguration;
private readonly string _provisioningConnectionName;
public ProvisionThenMigrateInitializer(string readWriteConnectionName, string provisioningConnectionName)
{
_provisioningConnectionName = provisioningConnectionName;
_readWriteConfiguration = new TConfiguration
{
TargetDatabase = new DbConnectionInfo(readWriteConnectionName)
};
}
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(false))
{
DbMigrator migrator = new DbMigrator(_readWriteConfiguration);
migrator.Update();
}
}
else
{
// TODO - Create the DB and user here...
string[] sqlStatements =
{
"CREATE DATABASE [database_name] CONTAINMENT = PARTIAL ",
"USE [database_name]",
"CREATE USER [app_user] WITH PASSWORD=N'p#ssw0rd'",
"USE [database_name]",
"ALTER ROLE [db_datareader] ADD MEMBER [app_user]",
"ALTER ROLE [db_datawriter] ADD MEMBER [app_user]",
};
string connectionString = ConfigurationManager.ConnectionStrings[_provisioningConnectionName].ConnectionString;
SqlConnection sqlConnection = new SqlConnection(connectionString);
foreach (SqlCommand command in sqlStatements.Select(sqlStatement => new SqlCommand(sqlStatement, sqlConnection)))
{
command.ExecuteNonQuery();
}
context.Database.Create();
Seed(context);
context.SaveChanges();
}
}
I set the initializer to use in the static constructor of my DbContext derived class:
Database.SetInitializer(new ProvisionThenMigrateInitializer<Context, Configuration>(
DOMAIN_MODEL_CONNECTION_STRING_NAME,
DOMAIN_MODEL_PROVISIONING_CONNECTION_STRING_NAME));
However, when I attempt to use my fancy new custom database initializer, in the following manner, it just plain doesn't work:
using (Context c = new Context())
{
try
{
c.Database.Initialize(true);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
I think that by the time I attempt to call c.Database.Initialize(true) EF has already attempted to connect to the database (using the app_user credentials, not the "provisioning credentials), the connection attempt fails, and we bomb out.
Is it actually possible to use EF 6, Code First and Migrations in a way that will allow this provisioning of my database? If so, what am I doing wrong?
Many thanks.
Here's how I did it:
I have an 'admin' SQL login that is a member of the 'dbcreator' and 'securityadmin' fixed server roles.
I have two connection strings: one specifying the 'admin' sql login, and the other specifying the sql login name I have reserved for use by tenant connections to the db. The 'tenant' login gets created via the initial migration and is granted only reader and writer access to the domain model database.
I have a domain model.
I have my DbContext class.
I have a parameterless constructor on my DbContext class which specifies the ADMIN connection string and is intended to be used to run migrations; and I have another constructor which specifies the TENANT connection string and is the constructor used through the code for all db access made in the context of a logged on tenant user.
public Context()
: base(ADMIN_CONNECTION_STRING_NAME)
{
//etc
and
public Context(int tenantOrgId)
: base(TENANT_CONNECTION_STRING_NAME)
{
Prior to enabling migrations, I used the DbContext in a unit test, which resulted in EF Code First creating the DB catalog.
I have enabled migrations which has produced an inital DbMigration.
I then edited the initial DbMigration "Up" method to provision the tenant sql login and grant it membership to the reader and writer roles:
public override void Up()
{
SqlConnectionStringBuilder domainModelConnectionStringBuilder = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings[Context.TENANT_CONNECTION_STRING_NAME].ConnectionString);
string domainModelDatabaseName = domainModelConnectionStringBuilder.InitialCatalog;
Sql(string.Format("IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = 'gsp_domainmodel_tenant') CREATE LOGIN [gsp_domainmodel_tenant] WITH PASSWORD=N'ge0sp#tia!', DEFAULT_DATABASE=[{0}], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF", domainModelDatabaseName));
Sql(string.Format("USE [{0}]", domainModelDatabaseName));
Sql(string.Format("IF NOT EXISTS (SELECT * FROM [{0}].sys.database_principals WHERE name = 'gsp_domainmodel_tenant') CREATE USER [gsp_domainmodel_tenant] FOR LOGIN [gsp_domainmodel_tenant] WITH DEFAULT_SCHEMA=[gsp]", domainModelDatabaseName));
Sql(string.Format("USE [{0}]", domainModelDatabaseName));
Sql(string.Format("ALTER ROLE [db_datareader] ADD MEMBER [gsp_domainmodel_tenant]", domainModelDatabaseName));
Sql(string.Format("USE [{0}]", domainModelDatabaseName));
Sql(string.Format("ALTER ROLE [db_datawriter] ADD MEMBER [gsp_domainmodel_tenant]", domainModelDatabaseName));
CreateTable( //etc
That is all you need to do if you are happy with the team using Update-Database to apply migrations to their local DBs, and you are happy with exec'ing Migrate.exe on the command line to deploy the db on your build machine, and you are happy with deploying the db changes to production using your own wits.
You can go one step further and specify the MigrateDatabaseToLatestVersion initializer to automate deploying the migrations, both on local dev workstations and on environments that you deploy to.
The trick is that you need to make the MigrateDatabaseToLatestVersion initializer run using the parameterless DbContext constructor so that the migrations are applied in the context of the ADMIN sql login (not the TENANT). That is achieved thusly:
static Context()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion());
// Make the initializer run now, with the parameterless constructor, such that the migrations are run using the admin connection string.
using(var initializerCtx = new Context())
{
initializerCtx.Database.Initialize(true);
}
}
You should be able to do what you want. Key to the matter is making sure context is accessed / renewed with right connection details.
Call the Migrate Method when it suits you in your code.
Change MigrateDatabaseToLatestVersion to match you migration strategy.
EDIT: I will try and summarise the idea and show a snippet sample.
Essentially I use a LUW class that defaults to DONT TOUCH DB.
The Luw needs the DBServer and DBName in constructor
I have a tool that gets the DBConnection for SQL Server
From an Admin ui I have a button. Migrate.
I can then trigger automatic migration when it suits.
I currently use Automatic. But this concept woudl apply fine to managed migrations.
public class Luw{
public Luw(string dataSource, string dbName ){ // constructor
Context = GetContext(dataSource, dbName );
}
public override void MigrateDb() {
// i put this method in my UoW class, I trigger Migrations when I want them to start.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MYDbContext, MYSECIALMigrationConfiguration>());
// Context = GetDefaultContext(); //HERE GET THE CONTEXT WITH CORRECT CONNECTION INFO
Context.Database.Initialize(true);
}
public static MyDbContext GetContext(string dataSource, string dbName)
{
Database.SetInitializer(new ContextInitializerNone<MyDbContext>());
return new MyDbContext((MYTOOLS.GetSQLConn4DBName(dataSource,dbName )),true);
}
public class MYSPECIALMigrationConfiguration : MYBaseMigrationConfiguration<MYDbContext>{ }
public abstract class MYBaseMigrationConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext{
protected MYBaseMigrationConfiguration() {
AutomaticMigrationsEnabled = true; // you can still chnage this later if you do so before triggering Update
AutomaticMigrationDataLossAllowed = true; // you can still chnage this later if you do so before triggering Update
}
public clas SQLTOOLS{
// ..... for SQL server....
public DbConnection GetSqlConn4DbName(string dataSource, string dbName) {
var sqlConnStringBuilder = new SqlConnectionStringBuilder();
sqlConnStringBuilder.DataSource = String.IsNullOrEmpty(dataSource) ? DefaultDataSource : dataSource;
sqlConnStringBuilder.IntegratedSecurity = true;
sqlConnStringBuilder.MultipleActiveResultSets = true;
var sqlConnFact = new SqlConnectionFactory(sqlConnStringBuilder.ConnectionString);
var sqlConn = sqlConnFact.CreateConnection(dbName);
return sqlConn;
}

Entity Framework - Database First without config

I'm developing a class library that deals with an exiting db using EF. I want to avoid the consumer of the class library (and .exe or a web site) to have in the *.config file the Entity connection string. I want the connection string set a run-time.
How do I set the connection string with Database First approach? There is no constructor overload that takes a connection string and when I created one (in a separate partial class) I got an "UnintentionalCodeFirstException".
I have reviewed already the following links:
Is there a way to change connection string in database first?. Its about modifying the connection string in the config file, which I want to avoid, also because it would recycle the process (in the case of a web app)
How can l use Entity Framework without App.config. Not good because it uses ObjectContext and I need the context generated when I imported the database.
There is a constructor on DbContext that takes a DbConnection, and you need to use an EntityConnection object for it:
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = "server name";
sqlBuilder.InitialCatalog = "database name";
sqlBuilder.IntegratedSecurity = true;
// Build the SqlConnection connection string.
string providerString = sqlBuilder.ToString();
var entityBuilder = new EntityConnectionStringBuilder();
// Initialize the EntityConnectionStringBuilder.
//Set the provider name.
entityBuilder.Provider = "System.Data.SqlClient";
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl";
using(var context = new YourDbContext(entityBuilder.ToString())){
//do stuff here
}
The important thing to note is the metadata part - "Model1" obviously needs to be replaced for your model name.
Ref: http://msdn.microsoft.com/en-us/library/bb738533.aspx
EDIT 20/02/2013 22:25
So as an addition you'll need to extend the created DbContext class with a partial class that adds a constructor to support the above code, like this:
public partial class YourDbContext
{
public YourDbContext(string connection) : base(connection) {}
}
This class needs to be in the same namespace as the DbContext that is generated by the entity framework wizard.

WCF with Entity Framework Error

Error: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I am trying to create a WCF service with Entity Framework (VS 2010, .NET 4). When I run it, I get the above error.
I read something about editing the T4 template, but it appears that it already has
[DataContractAttribute(IsReference=true)]
public partial class Person : EntityObject
and
[DataMemberAttribute()]
public global::System.Int32 ID
{
get
{
return _ID;
}
I am not sure what the difference is between
[DataMemberAttribute()] and [DataMember]
or
[DataContractAttribute(IsReference=true)] and [DataContract]
either.
public Person GetPersonByID(int id)
{
using (var ctx = new MyEntities())
{
return (from p in ctx.Person
where p.ID == id
select p).FirstOrDefault();
}
}
How does WCF and EF work together, properly?
Do you have navigation properties in your Person class? Did you disable lazy loading? Otherwise it will probably try to load content for navigation properties during serialization and it fails because of closed context.
To your other questions:
[DataMemberAttribute()] and [DataMember] are same. It is just shorter name.
[DataContractAttribute(IsReference=true)] and [DataContract] are not same. IsRefrence allows tracking circular references in navigation properties. Without this parameter circular reference causes never ending recursion.

Single Connection String with Multiple Entity Framework Models?

At work we currently have a very large web application with a connection to a massive database. We have been using Entity Framework for a while now and to make things easier we divided the database into many Entity models. This works well for us but we ran into an issue. Each EF model needs its own connection string due to the metadata part of the connection string. Managing so many connection string is a pain.
Now I have a solution that I think will work. I am going to create a class that will have the metadata info saved as a property also concatenated to the standard connection string in the web.config. So when we use the connection string "Database.EntityConnectionString" it will give me the Entity Connection string but we only have to manage a single connection string in the web.config. We will still have to manage the class with the metadata but Models don't change very much and we don't create them often so maintenance should be fine. My question, is there a better way of dealing with this issue or how would you do it?
Thanks!
This is how I have implemented my solution to this problem:
namespace DBLibrary
{
public enum Models
{
Model1,
Model2
}
public static class Database
{
public static string EntitiesConnectionString(Models model)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings["Default"].ConnectionString);
builder["MultipleActiveResultSets"] = true;
builder["Connect Timeout"] = 30;
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
entityBuilder.Provider = "System.Data.SqlClient";
entityBuilder.ProviderConnectionString = builder.ConnectionString;
switch (model)
{
case Models.Model1:
entityBuilder.Metadata = "res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl";
return entityBuilder.ToString();
case Models.Model2:
entityBuilder.Metadata = "res://*/Model2.csdl|res://*/Model2.ssdl|res://*/Model2.msl";
return entityBuilder.ToString();
default:
throw new Exception("Invalid model, no connection string defined");
}
}
}
}
I still need to clean up the code and all but I think this give you a good idea on how this can be implemented. I would still be very interested if there are different and better ways of doing this.
Thanks!
Add Default Construction in your Class
public class ItemContext : DbContext
{
public DbSet<Item>Items get; set; }
public DbSet<ItemDetail> ItemDetails { get; set; }
public ItemContext ()
{
this.Database.Connection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
}
}
I had the same problem. I have solved it by following way:
I have created two edmx file, but while creating second edmx file, i ignored the connection string to be save in config file. This way my config file will hold only one Connection string.
Then i modified following lines in my connection string:
<add name="MyDbContext" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="data source=abc;initial catalog=mydb;persist security info=True;user id=myuser;password=password;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
Just replace "res://model1.csdl" with "res://*/" and it works like a charm.
You can specify this connection name in constructor of your dbcontext class like:
public MyDbContext() : base("name=NameOfYourConnectionString") // Name of your connection string
{ }
Note: I am using Entity Framework 5.0.