I've an application which insert/save data in different databases hosted on different server. UI may be different but at the end data which is getting saved is almost same.
So i want to use the same DataAccessLayer but want to change the connectionString based on the loggedin user.
Dependency can be configured in startup.cs but at that time i may not know the DataBase user would like to work with.
on login page i'm asking user to select the database to work with, so only way to change the connection string is after login page.
Any suggestion?
public class ConnectionRepository : IConnectionRepository
{
private IDbConnection _cnn = null;
public IDbConnection GetOpenConnection(string databaseName)
{
if (_cnn != null && _cnn.ConnectionString.ToLower().Contains(databaseName.ToLower()))
{
_cnn.Open();
return _cnn;
}
var cnn = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
//Now replace database name in connection string with whichever one supplied
var cb = new SqlConnectionStringBuilder(cnn) { InitialCatalog = databaseName };
// wrap the connection with a profiling connection that tracks timings
return new ProfiledDbConnection(new SqlConnection(cb.ConnectionString), MiniProfiler.Current);
}
}
This code replaces the InitialCatalog (database name) part of the connection string dynamically base on the supplied name. The current name is stored in session when the user logs in.
Hope this helps.
I have my entity model and connection set up, and as you probably know when you set up the connection to be stored in the config file, it recommends you don't store the "sensitive" data (i.e. user name and password) in the config file, well what I wanted to do was allow the user to enter that information themselves.
How do I assign it to the connection in code?
Do I have to pull the string, modify it (by adding the user/pass) and then reassign the connection string?
sounds like a desktop (not a web app), correct? since you are probably not running over the internet but rather in a local network why don't you use integrated (windows) security instead of the sql server security and hot have to store the login/password at all.
public class MyContext : DbContext
{
// Add a constructor that takes a connection string
public MyContext(string connString)
: base(connString)
{
}
}
// Call this method from a page or controller
public void ConnectToTheDatabase(string username, string password)
{
// create the connection string; I like to user the builder
System.Data.Common.DbConnectionStringBuilder builder
= new System.Data.Common.DbConnectionStringBuilder();
builder.Add("Server", "tcp:asdfewsdfgwe.database.windows.net,1422");
builder.Add("Database", "supersonic_db");
builder.Add("User ID", username);
builder.Add("Password", password);
builder.Add("Trusted_Connection", "False");
builder.Add("Encrypt", "True");
builder.Add("Connection Timeout", "30");
var connString = builder.ToString();
// Set the connection string
MyContext context = new MyContext(connString);
// Test with something simple
context.Database.Connection.Open();
string version = context.Database.Connection.ServerVersion;
version = version.ToUpper();
}
I am using C# with xamarin studio. I can't find a working example to do an update statement against my db. I am trying to update a value of a record from 50 to 100.
This class represents one record in db:
[Table("record")]
public class Record
{
[PrimaryKey, AutoIncrementAttribute, Column("id")]
public int ID {get; set;}
[Column("value")]
public string Value {get; set;}
}
I can retrieve my records fine using:
string pathToDatabase = "mydb.db";
var db = new SqliteConnection (pathToDatabase);
myRecords = db.Query<Record>("SELECT * FROM records;");
The update should be as simple as doing Get and then Update.
var recToUpdate = db.Get<Record>(1); // record with primary key of 1.
recToUpdate.Value = "100"; // instead of 50
db.Update(recToUpdate);
It executes the update line fine, but the db still holds the older value of 50 instead of 100, when I run the app again.
Is my approach totally wrong?
If you are including an existing db in your app bundle, you cannot write to it. The app bundle is read only - this is an iOS security measure. In order to write to your db, you will need to move it to a user writable folder.
// in your app startup
string rootPath = "/mydb.db";
string userPath = Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "mydb.db";
// if userdb does not exist, copy it from the app bundle
if (!File.Exists(userPath)) {
File.Copy(rootPath, userPath);
}
When you want to actually access your db, you just use the db access code you already have, but be sure you are using the user writable path.
var db = new SqliteConnection (userPath);
Finally, if your db is read-only and will not be updated by the user, you can leave it in the app bundle and don't need to make a writable copy.
try with
recToUpdate.Value = "100"; // instead of 50
int i = db.Update(recToUpdate);
//check number of rows updated (i), db.Update() returns number of rows updated...
//or first try with recToUpdate.Value = 100; and see the number of rows affected + use try/catch if an error appears.
I have a web API project which references my model and DAL assemblies. The user is presented with a login screen, where he can select different databases.
I build the connection string as follows:
public void Connect(Database database)
{
//Build an SQL connection string
SqlConnectionStringBuilder sqlString = new SqlConnectionStringBuilder()
{
DataSource = database.Server,
InitialCatalog = database.Catalog,
UserID = database.Username,
Password = database.Password,
};
//Build an entity framework connection string
EntityConnectionStringBuilder entityString = new EntityConnectionStringBuilder()
{
Provider = database.Provider,
Metadata = Settings.Default.Metadata,
ProviderConnectionString = sqlString.ToString()
};
}
First of all, how do I actually change the connection of the data context?
And secondly, as this is a web API project, is the connection string (set at login per above) persistent throughout the user's interaction or should it be passed every time to my data context?
A bit late on this answer but I think there's a potential way to do this with a neat little extension method. We can take advantage of the EF convention over configuration plus a few little framework calls.
Anyway, the commented code and example usage:
extension method class:
public static class ConnectionTools
{
// all params are optional
public static void ChangeDatabase(
this DbContext source,
string initialCatalog = "",
string dataSource = "",
string userId = "",
string password = "",
bool integratedSecuity = true,
string configConnectionStringName = "")
/* this would be used if the
* connectionString name varied from
* the base EF class name */
{
try
{
// use the const name if it's not null, otherwise
// using the convention of connection string = EF contextname
// grab the type name and we're done
var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
? source.GetType().Name
: configConnectionStringName;
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString);
// init the sqlbuilder with the full EF connectionstring cargo
var sqlCnxStringBuilder = new SqlConnectionStringBuilder
(entityCnxStringBuilder.ProviderConnectionString);
// only populate parameters with values if added
if (!string.IsNullOrEmpty(initialCatalog))
sqlCnxStringBuilder.InitialCatalog = initialCatalog;
if (!string.IsNullOrEmpty(dataSource))
sqlCnxStringBuilder.DataSource = dataSource;
if (!string.IsNullOrEmpty(userId))
sqlCnxStringBuilder.UserID = userId;
if (!string.IsNullOrEmpty(password))
sqlCnxStringBuilder.Password = password;
// set the integrated security status
sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;
// now flip the properties that were changed
source.Database.Connection.ConnectionString
= sqlCnxStringBuilder.ConnectionString;
}
catch (Exception ex)
{
// set log item if required
}
}
}
basic usage:
// assumes a connectionString name in .config of MyDbEntities
var selectedDb = new MyDbEntities();
// so only reference the changed properties
// using the object parameters by name
selectedDb.ChangeDatabase
(
initialCatalog: "name-of-another-initialcatalog",
userId: "jackthelady",
password: "nomoresecrets",
dataSource: #".\sqlexpress" // could be ip address 120.273.435.167 etc
);
I know you already have the basic functionality in place, but thought this would add a little diversity.
DbContext has a constructor overload that accepts the name of a connection string or a connection string itself. Implement your own version and pass it to the base constructor:
public class MyDbContext : DbContext
{
public MyDbContext( string nameOrConnectionString )
: base( nameOrConnectionString )
{
}
}
Then simply pass the name of a configured connection string or a connection string itself when you instantiate your DbContext
var context = new MyDbContext( "..." );
Jim Tollan's answer works great, but I got the Error: Keyword not supported 'data source'.
To solve this problem I had to change this part of his code:
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString);
to this:
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
{
ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString).ConnectionString
};
I'm really sorry. I know that I should't use answers to respond to other answers, but my answer is too long for a comment :(
The created class is 'partial'!
public partial class Database1Entities1 : DbContext
{
public Database1Entities1()
: base("name=Database1Entities1")
{
}
... and you call it like this:
using (var ctx = new Database1Entities1())
{
#if DEBUG
ctx.Database.Log = Console.Write;
#endif
so, you need only create a partial own class file for original auto-generated class (with same class name!) and add a new constructor with connection string parameter, like Moho's answer before.
After it you able to use parametrized constructor against original. :-)
example:
using (var ctx = new Database1Entities1(myOwnConnectionString))
{
#if DEBUG
ctx.Database.Log = Console.Write;
#endif
You can do this on-the-fly with an IDbConnectionInterceptor. This has the advantage of allowing you to work with a standard connection string and not the Entity Client version, and also not having to modify the auto-generated context classes in an EDMX model, or using overloaded constructors. It just works!
We use this, for instance, to replace a tokenized connection string with a password from a secrets vault.
First, implement the interface. I'm only showing one of the many interface methods that will need to be implemented. In this case, I'm implementing ConnectionStringGetting, and leaving all other method bodies empty:
public class SecretsDbConnectionInterceptor : IDbConnectionInterceptor
{
public void ConnectionStringGetting(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
{
var originalConnectionString = connection.ConnectionString;
try
{
connection.ConnectionString = /* Build your new connection string */;
}
catch (Exception e)
{
connection.ConnectionString = originalConnectionString;
Trace.WriteLine(e.Message);
}
}
// ... Many other methods here; leave them empty
}
You can wire this up via your .config file; just add an <interceptor /> to the existing <entityFramework /> node with your new inteceptor's fully qualified type name:
<entityFramework>
<interceptors>
<interceptor type="Foo.Bar.SecretsDbConnectionInterceptor, Foo.Bar" />
</interceptors>
...
</entityFramework>
Or, my personal preference, you can wire it up via code. It is equivalent to the config version. Ideally this would go in an Application_Startup in a service/UI project, or towards the top of Main in a console app, because it must run before you attempt to establish any new DbContexts:
DbInterception.Add(new Foo.Bar.SecretsDbConnectionInterceptor());
When you configure via code, you could pass parameters to your interceptor constructor, or use DI.
Note: the interceptor code runs every time you create a new instance of any DbContext in your application, so beware of performance impacts. You could implement some caching strategy within your interceptor, or make it a singleton instance with a context name/connection string mapping, or something smart like that.
Add multiple connection strings in your web.config or app.config.
Then you can get them as a string like :
System.Configuration.ConfigurationManager.
ConnectionStrings["entityFrameworkConnection"].ConnectionString;
Then use the string to set :
Provider
Metadata
ProviderConnectionString
It is better explained here :
Read connection string from web.config
string _connString = "metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=DATABASE;persist security info=True;user id=sa;password=YourPassword;multipleactiveresultsets=True;App=EntityFramework"";
EntityConnectionStringBuilder ecsb = new EntityConnectionStringBuilder(_connString);
ctx = new Entities(_connString);
You can get the connection string from the web.config, and just set that in the EntityConnectionStringBuilder constructor, and use the EntityConnectionStringBuilder as an argument in the constructor for the context.
Cache the connection string by username. Simple example using a couple of generic methods to handle adding/retrieving from cache.
private static readonly ObjectCache cache = MemoryCache.Default;
// add to cache
AddToCache<string>(username, value);
// get from cache
string value = GetFromCache<string>(username);
if (value != null)
{
// got item, do something with it.
}
else
{
// item does not exist in cache.
}
public void AddToCache<T>(string token, T item)
{
cache.Add(token, item, DateTime.Now.AddMinutes(1));
}
public T GetFromCache<T>(string cacheKey) where T : class
{
try
{
return (T)cache[cacheKey];
}
catch
{
return null;
}
}
In my case I'm using the ObjectContext as opposed to the DbContext so I tweaked the code in the accepted answer for that purpose.
public static class ConnectionTools
{
public static void ChangeDatabase(
this ObjectContext source,
string initialCatalog = "",
string dataSource = "",
string userId = "",
string password = "",
bool integratedSecuity = true,
string configConnectionStringName = "")
{
try
{
// use the const name if it's not null, otherwise
// using the convention of connection string = EF contextname
// grab the type name and we're done
var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
? Source.GetType().Name
: configConnectionStringName;
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString);
// init the sqlbuilder with the full EF connectionstring cargo
var sqlCnxStringBuilder = new SqlConnectionStringBuilder
(entityCnxStringBuilder.ProviderConnectionString);
// only populate parameters with values if added
if (!string.IsNullOrEmpty(initialCatalog))
sqlCnxStringBuilder.InitialCatalog = initialCatalog;
if (!string.IsNullOrEmpty(dataSource))
sqlCnxStringBuilder.DataSource = dataSource;
if (!string.IsNullOrEmpty(userId))
sqlCnxStringBuilder.UserID = userId;
if (!string.IsNullOrEmpty(password))
sqlCnxStringBuilder.Password = password;
// set the integrated security status
sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;
// now flip the properties that were changed
source.Connection.ConnectionString
= sqlCnxStringBuilder.ConnectionString;
}
catch (Exception ex)
{
// set log item if required
}
}
}
I wanted to have multiple datasources in the app config. So after setting up a section in the app.config i swaped out the datasource and then pass it into the dbcontext as the connection string.
//Get the key/value connection string from app config
var sect = (NameValueCollection)ConfigurationManager.GetSection("section");
var val = sect["New DataSource"].ToString();
//Get the original connection string with the full payload
var entityCnxStringBuilder = new EntityConnectionStringBuilder(ConfigurationManager.ConnectionStrings["OriginalStringBuiltByADO.Net"].ConnectionString);
//Swap out the provider specific connection string
entityCnxStringBuilder.ProviderConnectionString = val;
//Return the payload with the change in connection string.
return entityCnxStringBuilder.ConnectionString;
This took me a bit to figure out. I hope it helps someone out. I was making it way too complicated. before this.
I have two extension methods to convert the normal connection string to the Entity Framework format. This version working well with class library projects without copying the connection strings from app.config file to the primary project. This is VB.Net but easy to convert to C#.
Public Module Extensions
<Extension>
Public Function ToEntityConnectionString(ByRef sqlClientConnStr As String, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True)
Dim sqlb As New SqlConnectionStringBuilder(sqlClientConnStr)
Return ToEntityConnectionString(sqlb, modelFileName, multipleActiceResultSet)
End Function
<Extension>
Public Function ToEntityConnectionString(ByRef sqlClientConnStrBldr As SqlConnectionStringBuilder, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True)
sqlClientConnStrBldr.MultipleActiveResultSets = multipleActiceResultSet
sqlClientConnStrBldr.ApplicationName = "EntityFramework"
Dim metaData As String = "metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string='{1}'"
Return String.Format(metaData, modelFileName, sqlClientConnStrBldr.ConnectionString)
End Function
End Module
After that I create a partial class for DbContext:
Partial Public Class DlmsDataContext
Public Shared Property ModelFileName As String = "AvrEntities" ' (AvrEntities.edmx)
Public Sub New(ByVal avrConnectionString As String)
MyBase.New(CStr(avrConnectionString.ToEntityConnectionString(ModelFileName, True)))
End Sub
End Class
Creating a query:
Dim newConnectionString As String = "Data Source=.\SQLEXPRESS;Initial Catalog=DB;Persist Security Info=True;User ID=sa;Password=pass"
Using ctx As New DlmsDataContext(newConnectionString)
' ...
ctx.SaveChanges()
End Using
For both SQL Server and SQLite Databases, use:
_sqlServerDBsContext = new SqlServerDBsContext(new DbContextOptionsBuilder<SqlServerDBsContext>().UseSqlServer("Connection String to SQL DB").Options);
For SQLite, make sure Microsoft.EntityFrameworkCore.Sqlite is
installed, then the connection string is simply "'DataSource='+ the file name".
_sqliteDBsContext = new SqliteDBsContext(new DbContextOptionsBuilder<SqliteDBsContext>().UseSqlite("Connection String to SQLite DB").Options);
well if you are working with EFCore, Then You can do something like to create a new connection string:
In Your Context File (For Sqlite)
public biorevContext(string connectionString) : base(GetOptions(connectionString))
{
this.Database.EnsureCreated();
}
private static DbContextOptions GetOptions(string connectionString)
{
return SqliteDbContextOptionsBuilderExtensions.UseSqlite(new DbContextOptionsBuilder(), connectionString).Options;
}
For MySql:
public biorevContext(string connectionString) : base(GetOptions(connectionString))
{
this.Database.EnsureCreated();
}
private static DbContextOptions GetOptions(string connectionString)
{
return MySQLDbContextOptionsExtensions.UseMySQL(new DbContextOptionsBuilder(), connectionString).Options;
}
For Sql:
public biorevContext(string connectionString) : base(GetOptions(connectionString))
{
this.Database.EnsureCreated();
}
private static DbContextOptions GetOptions(string connectionString)
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}
and Then You can use it like this:
var context = new biorevContext("connectionString");
Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext();
var query = from p in db.SyncAudits orderby p.SyncTime descending select p;
Console.WriteLine(query.ToString());
try this code...
I have an EF5 ASP.NET MVC 3 (Razor) web site, running under IIS7. Now I want to be able to change the connection string to the MSSQL database depending on the subdomain of the URL, e.g. foo.mydomain.com should connect to my "Foo" database, and bar.mydomain.com should connect to the "Bar" database.
Obviously the DNS records are set up so that they all point to the same web site.
What's the most efficient way of achieving this?
why don't you start passing your own SqlConnection to your YourDbContext?
var partialConString = ConfigurationManager.ConnectionStrings["DBConnectionStringName"].ConnectionString;
var connection = new SqlConnection("Initial Catalog=" + Request.Url.Host + ";" + partialConString);
var context = new MyDbContext(connection, true);
You can also change database in the DBContext:
context.Database.Connection.ChangeDatabase("newDbname");
It's not very easy...
You should change the constructor of object context to dynamically change the connection string.
Take the subdomain name using System.Web.HttpContext.Current.Request.Url.Host. Then use it to compute the proper connection string.
You should do this in the designer generated code. Of course this is not a good place.. to make it work use the T4 templating. Open your model and right click on the blank designer surface, then select "Add code generation item" -> Ado.net entity object generation. This will create a .tt file. Open it and look for the constructor syntax. Add your logic there.
Good luck!
I've come up with what I feel is a better solution than all those proposed to date. I'm using the default EntityModelCodeGenerator, so perhaps there are other, better, solutions for other templates - but this works for me:
Create the other half of the partial class MyEntities.
Override OnContextCreated(), which is called from within the class constructor.
Change the store connection string using a regex.
This comes out as follows:
partial void OnContextCreated()
{
// change connection string, depending on subdomain
if (HttpContext.Current == null) return;
var host = HttpContext.Current.Request.Url.Host;
var subdomain = host.Split('.')[0];
switch (subdomain)
{
case "foo":
ChangeDB("Foo");
break;
case "bar":
ChangeDB("Bar");
break;
}
}
private void ChangeDB(string dbName)
{
var ec = Connection as EntityConnection;
if (ec == null) return;
var match = Regex.Match(ec.StoreConnection.ConnectionString, #"Initial Catalog\s*=.*?;", RegexOptions.IgnoreCase);
if (!match.Success) return;
var newDbString = "initial catalog={0};".Fmt(dbName);
ec.StoreConnection.ConnectionString = ec.StoreConnection.ConnectionString.Replace(match.Value, newDbString);
}
Either use different connection strings in the web.config. Maybe research a bit if you can have conditional XSL transformations, that way, when you publish on a specific configuration the web.Release.config will change your Web.Config to be what you need it to be.
Or, use |DataDirectory| string substitution - http://msdn.microsoft.com/en-us/library/cc716756.aspx
more on DataDirectory string substitution here:
http://forums.asp.net/t/1835930.aspx/1?Problem+With+Database+Connection
I guess, if you want to be by the book, create build configurations for each of your separate releases and put the connection string in the respective web..config and when you publish, that XSL transformation will put the connection string in the resulting web.config and voila.
I've done something like that recently by adding some custom configuration, which uses the host header to determine the connectionStringName, which has to be used.
EF5 has a constructor, which can handle this name
var context = new MyDbContex("name=<DBConnectionStringName>");
I just did for a project
public partial class admpDBcontext : DbContext
{
public static string name
{
get
{
if (System.Web.HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority).ToString() == "http://fcoutl.vogmtl.com")
{
return "name=admpDBcontext";
}
else { return "name=admpNyDBcontext"; }
}
}
public admpDBcontext()
: base(name)
{
}
}
And in the web.config I add the connectionString.