How to set a EF code first connection string at runtime - c#

I'm missing something. I have a small project where I am trying to insert some data into a database. When I created the model I used Code First against an existing database. I wanted to make the app capable of point to any server at runtime. This is a down and dirty utility app an and running internally but I need some flexibility when running it.
From reading some msdn articles I saw where I could extend the DBContext on initialization so I did the following:
added a parameter to the initialization:
public OtherEventModel(string ConnectionString)
: base("name=OtherEventModel")
{...
Then within my code I built the connection string based upon the properties passed in from the UI:
private OtherEventModel ConnectToServer(string server, string username, string password)
{
try
{
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder
{
DataSource = server,
UserID = username,
Password = password,
InitialCatalog = "My_Database",
IntegratedSecurity = true
};
var connection = sqlBuilder.ToString();
ModelContext = new OtherEventModel(connection);
return ModelContext;
}
catch (Exception ex)
{
throw new Exception(string.Format("Error connecting to server {0}", ex.Message));
}
I can see the connection string created however when I run the code I receive an exception that the connection string (what should be in app.config could not be found). Which I do not have a connection string or key within app config since I was trying to inject the connection string in at runtime.
Error:
Error inserting data No connection string named 'OtherEventModel' could be found in the application config file
I was under the impression from what I have read that Code first uses a standard SQL connection string and does not require the EntityConnectionStringBuild (used for object first dev)
Can someone point me in the right direction as to what I have missed or what I am doing wrong.
Thanks in advance

You're almost there, just pass the connection string to the base:
public OtherEventModel(string ConnectionString)
: base(ConnectionString)
Make sure you comment the throw line in the method below. Unfortunately you have to do this again every time you update the model.

Related

C# DbContext Connection Type and Server

I have a WinForm application developed on one laptop connected to an SQL server on the same laptop.
I have a new laptop and have created a docker setup for an SQL server. I am looking to change the code base to use the new SQL server.
The new server is using SQL Server auth with username and password on the new laptop. The old laptop is using windows authentication on a windows installed setup. I have migrated a copy of the entire DB into my dockerised instance of the sql server.
The application has the connection settings in the app config and naturally this is for windows authentication.
My app.config is comitted to my github repository. I do not want to store the sql user/password in the app.settings, but instead I would like to get these from env variables I set on the machine.
I would also like to know how to change the format of the connection string in app.config so it works with sql server authentication.
Or maybe now I have explained what I am trying to do, there might be a better way?
My current connection strings are
<connectionStrings>
<add name="Blah.Properties.Settings.BlahConnectionString"
connectionString="Data Source=W.....R....;
Initial Catalog=Blah;
Integrated Security=True;
Connect Timeout=30;Encrypt=False;
TrustServerCertificate=False"
providerName="System.Data.SqlClient"/>
</connectionStrings>
[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="Blah")]
public partial class BlahDBDataContext : System.Data.Linq.DataContext
I searched all code for 'AddDbContextFactory' and 'GetConnectionString'
public BlahDBDataContext() :
base(global::Blah.Properties.Settings.Default.BlahConnectionString, mappingSource)
{
OnCreated();
}
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=PCNAME;Initial Catalog=Blah;Integrated Security=True" + ";Connect Timeout=30;Encrypt=False;TrustServerCertificate=False")]
public string BlahConnectionString
{
get { return ((string)(this["BlahConnectionString"]));
}
The database context class is generated code inheriting as far as I can tell on System.Data.Linq.DataContext, as shown by this code from my application.
[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="Blah")]
public partial class BlahDBDataContext : System.Data.Linq.DataContext
{
The BlahDBDataContext class provides a number of constructors. Please check for yourself for a fuller list.
For my purposes the constructor I needed was
public BlahDBDataContext(System.Data.IDbConnection connection) :
base(connection, mappingSource)
{
OnCreated();
}
This requires the construction of a System.Data.IDbConnection object to hold/manage the connection details.
Therefore in my case all I needed to do was construct the connection string, for example.
string userName = EnvironmentVariables.GetValue("BLAH_USERNAME");
string password = EnvironmentVariables.GetValue("BLAH_PASSWORD");
string server = EnvironmentVariables.GetValue("BLAH_SERVER");
string database = EnvironmentVariables.GetValue("BLAH_DATABASE");
connectionString = $#"Data Source={server};Initial Catalog={database};Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;User ID={userName};Password={password};";
and then all thats needed is
new BlahDBDataContext(
new SqlConnection(
Program.GetDatabaseConnectionString()))
Where Program.GetDatabaseConnectionString() is.
public static string GetDatabaseConnectionString()
{
if (connectionString is null)
{
string userName = EnvironmentVariables.GetValue("BLAH_USERNAME");
string password = EnvironmentVariables.GetValue("BLAH_PASSWORD");
string server = EnvironmentVariables.GetValue("BLAH_SERVER");
string database = EnvironmentVariables.GetValue("BLAH_DATABASE");
connectionString = $#"Data Source={server};Initial Catalog={database};Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;User ID={userName};Password={password};";
}
return connectionString;
}
From a SOLID principles and clean coding perspective this is requires refactoring, and will be cleaned up now I have working code.

The underlying provider failed on Open at the time Unity resolve the dependencies

I am using an MVC Application that works with Unity to register all the dependencies of my project. I am also using Entity Framework for the DAL.
In my local machine the application is running fine but at the time I deployed on the server I am receiving an error when Unity is trying to resolve the dependencies.
The underlying provider failed on Open. ... At the time of the exception, the container was: Resolving Class mapped from IClass ... Calling constructor of MyDbContext(System.String connectionName)
1- Does this have a relation with the connection string structure?
Bellow is my connection string
<add name="DBConnection" connectionString="Server=MyServer;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
At this moment I don't have the access to the DB to check if the IIS user has the privileges on the server to run any query, but supposing it doesn't, does this has any relation with the error at the time Unity is trying to resolve the connection string?
2- Does Unity try to open a connection to the DB once it tries to resolve the DBContext?
Here is how I am injecting the parameters
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["DBConnection"].ConnectionString;
container.RegisterType<IUnitOfWork, MyDbContext>(
Activator.CreateInstance<T>(),
new InjectionConstructor(connectionString));
Your problem is most probably tied to fact that your application pool identity on production doesn't have access to the db. Unity creates instance of all dependent classes once the need arises. So if your Class needs IUnitOfWork, unity tries to instantiate MyDbContext.
Let me point out that you shouldn't register instance of entity context (MyDbContext) as singleton in first place - entity framework context is not thread-safe (Entity Framework Thread Safety), also DI is part of critical infrastructure (there shouldn't be IO calls - such as opening the db that can go wrong)
Instead use something like
public interface IEntityContextProvider {
public MyDbContext CreateContext();
}
public class EntityContextProvider : IEntityContextProvider {
public MyDbContext CreateContext() {
return new MyDbContext("ef connection string");
}
}
Usage:
public class SomeClassUsingEF {
// DI
private readonly IEntityContextProvider _entityContextProvider;
// DI
public SomeClassUsingEF(IEntityContextProvider entityContextProvider)
{
if (entityContextProvider == null)
throw new ArgumentNullException(nameof(entityContextProvider));
_entityContextProvider = entityContextProvider;
}
public void SomeQueryToDB() {
using(var context = _entityContextProvider.CreateContext()) {
// select/insert/updates here
}
}
}
Also you connection string looks a lot like "ordinary" connection string - i.e. connection string to db wout entity framework - I'm missing the metadata in it. Code wise its usually something like
private string GetEFConnectionString()
{
string providerName = "System.Data.SqlClient";
string serverName = #".\SQL2008";
string databaseName = "my-db";
// Initialize the connection string builder for the
// underlying provider.
var sqlBuilder = new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.IntegratedSecurity = true;
// Build the SqlConnection connection string.
string providerString = sqlBuilder.ToString();
// Initialize the EntityConnectionStringBuilder.
var entityBuilder = new EntityConnectionStringBuilder();
// Set the provider name.
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/Model.csdl|
res://*/Model.ssdl|
res://*/Model.msl";
return entityBuilder.ToString();
}

Entity Framework 6.0 + Encrypt Connection String

This question may be repeated but we were not able to find a good solution.
Our project uses EF 6.0 & C# and targeted for .NET 4.0 and above and finally SQL Server 2012.
The project works fine without. But have few queries on the EF connection string and the way it stores.
How to read the encrypted connection string stored in the app.config to be read in the EF's context.cs file.
We added the following code to read encrypted connection string, it works but to an extent only i.e. till we don't add the a new stored procedure.
public MCMS_II_LogEntities()
// : base("name=MCMS_II_LogEntities") //Original
:base( GetConnectionString())
{
}
public static string GetConnectionString()
{
string connString = new System.Data.EntityClient.EntityConnectionStringBuilder
{
Metadata = "res://*",
Provider = "System.Data.SqlClient",
ProviderConnectionString = new System.Data.SqlClient.SqlConnectionStringBuilder
{
InitialCatalog = "<Catalog Name>",
DataSource = ConfigurationManager.AppSettings["DataSourceAddress"].ToString(),
IntegratedSecurity = false,
MultipleActiveResultSets = true,
UserID = "<LOGIN ID>", // User ID such as "sa"
Password ="<PASSWORD>", // hide the password
}.ConnectionString
}.ConnectionString;
return String.Format(connString);
}
Once we add the new stored procedure or function to the EF model, the above coded added will be removed.
Query
What is the best practice to achieve this?
How to address this issue.
During the development process you shouldn't encode/encrypt the db connection string, it's a pain in the butt. The encryption should be done during the deployment/promotion phase not during development.
Test the connection string AFTER you have encrypted it. Encrypt it when you are ready to move the code to QA or PROD.

Change Database during runtime in Entity Framework, without changing the Connection

I have a server that hosts 50 databases with identical schemas, and I want to start using Entity Framework in our next version.
I don't need a new connection for each of those databases. The privileges of the one connection can talk to all of the 50 databases, and for data management and speed (this is a WebAPI application) I don't want to instantiate a new EF context every time I talk to each of the databases if I don't have to, unless of course if this occurs each time a request comes to the server then no big deal.
All I really need is the ability to change the USE [databasename] command, which I assume eventually gets sent to the server from EF.
Is there a way to accomplish this in code? Does EF maintain a read/write property in the Context that refers to the database name that could be changed on the fly before calling SaveChanges(), etc.??
Thank you!!!
bob
Don't Work hard, work smart !!!!
MYContext localhostContext = new MYContext();
MYContext LiveContext = new MYContext();
//If your databases in different servers
LiveContext.Database.Connection.ConnectionString = LiveContext.Database.Connection.ConnectionString.Replace("localhost", "Live");
//If your databases have different Names
LiveContext.Database.Connection.ConnectionString = LiveContext.Database.Connection.ConnectionString.Replace("DBName-Localhost", "DBName-Live");
the structure for databases should be the same ;)
You can take a look at:
SO question about passing existing SQL Connection to
EntityFramework Context
and at this article describing how to
change database on existing connection.
Please let me know if any additional help is needed.
Edited
Updated 2nd link to point to SqlConnection.ChangeDatabase method.
So eventually code would look similarly to the following:
MetadataWorkspace workspace = new MetadataWorkspace(
new string[] { "res://*/" },
new Assembly[] { Assembly.GetExecutingAssembly() });
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
using (EntityConnection entityConnection = new EntityConnection(workspace, sqlConnection))
using (NorthwindEntities context = new NorthwindEntities(entityConnection))
{
// do whatever on default database
foreach (var product in context.Products)
{
Console.WriteLine(product.ProductName);
}
// switch database
sqlConnection.ChangeDatabase("Northwind");
Console.WriteLine("Database: {0}", connection.Database);
}
It is very simple
I had
public WMSEntities() : base("name=WMSEntities") //WMSEntities is conection string name in web.config also the name of EntityFramework
{
}
already in autogenerated Model.Context.cs of edmx folder.
To connect to multiple database in runtime, I created another constructor that takes connection string as parameter like below in same file Model.Context.cs
public WMSEntities(string connStringName)
: base("name=" + connStringName)
{
}
Now, I added other connection string in Web.Config for example
<add name="WMSEntities31" connectionString="data source=TESTDBSERVER_NAME;initial catalog=TESTDB;userid=TestUser;password=TestUserPW/>
<add name="WMSEntities" connectionString="data source=TESTDBSERVER_NAME12;initial catalog=TESTDB12;userid=TestUser12;password=TestUserPW12/>
Then, when connecting to database I call below method passing connectionString name as parameter
public static List<v_POVendor> GetPOVendorList(string connectionStringName)
{
using (WMSEntities db = new WMSEntities(connectionStringName))
{
vendorList = db.v_POVendor.ToList();
}
}
Here's my solution for just changing the database name. Simply pull the string from the web or app.config file, modify it, and then instantiate:
string yourConnection = ConfigurationManager.ConnectionStrings["MyEntities"].ConnectionString.Replace("MyDatabase", yourDatabaseName);
dcon = new MyEntities(yourConnection);
I have implemented this in my current project in which we have a common security database and different database for every client in the project. So our security database has a table that contain connection string for every other database. We just pass client id and get the connection string of the client database..
For this add two EDMX one for the common database and other for common schema databases. When user login or what might be your scenario to choose database go to common databse and get the connection string and create object of the needed database. Here is Code sample any, if any quer let me know..
You can keep connection string regarding every other database in a table in a a common database shared by all the other database.
EntityInstance_ReviewEntities.GetContext(GetConnectionString(ClientId));
private string GetConnectionString(int TenantId)
{
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
ISecurityRepository objSecurity = new SecurityRepository();
string tenantConnectionString = objSecurity.GetClientConnectionString(TenantId);
entityBuilder.ProviderConnectionString = tenantConnectionString;
entityBuilder.Provider = "System.Data.SqlClient";
entityBuilder.Metadata = #"res://*/ClientEntity.YourEntity.csdl|res://*/ClientEntity.ADBClientEntity.ssdl|res://*/ClientEntity.YourEntity.msl";
return entityBuilder.ToString();
}
EntityConnection.ChangeDatabase method is not supported, but SqlConnection.ChangeDatabase works fine.
So you have to use SqlConnection in entity framework database's constructor:
using MvcMyDefaultDatabase.Models;
using System.Data.Metadata.Edm;
using System.Data.SqlClient;
using System.Data.EntityClient;
using System.Configuration;
using System.Reflection;
public ActionResult List(string Schema)
{
SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
MetadataWorkspace workspace = new MetadataWorkspace(new string[] { "res://*/" }, new Assembly[] { Assembly.GetExecutingAssembly() });
EntityConnection entityConnection = new EntityConnection(workspace, sqlConnection);
sqlConnection.Open();
sqlConnection.ChangeDatabase(Schema);
Models.MyEntities db = new MyEntities(entityConnection);
List<MyTableRecords> MyTableRecordsList = db.MyTableRecords.ToList();
return View(MyTableRecordsList);
}
With this code you can read the tables with the same format (same table name and same fields) of several schema passing the database name in the "Schema" string.
For SQL Server, if you want to change only the database, not a connection, try:
public class XXXXDbContext : DbContext
{
public string databaseName
{
set
{
Database.GetDbConnection().Open();
Database.GetDbConnection().ChangeDatabase(value);
}
}
}

Moving connection string from app.config to code in C#

I'm trying to use Entity Framework and it put it's connection string into app.config. I would like to move it to code as it's easier for me at this stage of development.
metadata=res://*/database.csdl|res://*/database.ssdl|res://*/database.msl;provider=System.Data.SqlClient;provider connection string="data source=computer;initial catalog=database;persist security info=True;user id=user;password=Mabm#A;multipleactiveresultsets=True;App=EntityFramework"
How can I make Entity Framework use connection string from code rather then look at the app.config? Alternatively if it's not possible how can I pass parameters to app.config (like dbname, dbuser, dbpassword)?
You can use EntityConnectionStringBuilder for this purpose.
Check here
public string GetConnectionString()
{
string connectionString = new EntityConnectionStringBuilder
{
Metadata = "res://*/Data.System.csdl|res://*/Data.System.ssdl|res://*/Data.System.msl",
Provider = "System.Data.SqlClient",
ProviderConnectionString = new SqlConnectionStringBuilder
{
InitialCatalog = ConfigurationManager.AppSettings["SystemDBName"],
DataSource = ConfigurationManager.AppSettings["SystemDBServerName"],
IntegratedSecurity = false,
UserID = ConfigurationManager.AppSettings["SystemDBUsername"],
Password = ConfigurationManager.AppSettings["SystemDBPassword"],
MultipleActiveResultSets = true,
}.ConnectionString
}.ConnectionString;
return connectionString;
}
When you create an instance of your ObjectContext derived class, you can simply pass the connection string as a constructor argument.
Rather than use a username and password, why not use Integrated Security? It is more secure and easier to manage.
i.e. 'Trusted_Connection = Yes' in your connection string and securely manage access through AD.
Connection Strings
First, create your context using the constructor with the connectionString parameter.
http://msdn.microsoft.com/en-us/library/gg679467(v=vs.103).aspx
Note that it's not directly this constructor that you must call, but the specific inherited context constructor for your database that your Entity generator created for you.
Furthermore, if you want to pass the username and password at runtime, you can create a connection string using this class:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx
See here:
http://msdn.microsoft.com/en-us/library/bb738533.aspx
If the connection string log in details are always the same then I would suggest that you use ConfigurationManager to retrieve the connection string from your app.config and encrypt the ConnectionStrings section of the file.

Categories