Entity Framework - supply a connection string to constructor - entity-framework

I'm using EF in my C# project. We're about to add clients which will have other databases with other connection strings.
There's no need to sync between DBs.
I tried using the following c'tor (the 2nd out of 3 supplied):
public AppEntities(string connectionString)
and I get the following error: 'Keyword not supported: 'data source'.'
The connection string is as follows:
metadata=res://EntityFramework/App.csdl|res://EntityFramework/App.ssdl|res://EntityFramework/App.msl;provider=System.Data.SqlClient;provider connection string="Data Source=dbname\dbname;Initial Catalog=App;Persist Security Info=True;User ID=User;Password=password;MultipleActiveResultSets=True"
This is the same connection string that is used when taken from App.Config. I don't want to take it from there but rather build it myself (thus leaving the username & password hard-coded in my app's code and not in free text.
Any idea?
Thanks.

The connectionstring parameter is not an Entity Framework Connectionstring (as in the .config file) but it has to be a "normal" .net connectionstring.
The link's example shows how to "convert" the ef connectionstring to a normal connectionstring (using the EntityConnectionStringBuilder.ToString() method). If you pass the app.config's unprepared string you will get the mentioned error.
You can use the EntityConnectionStringBuilder to parse the app.config value and convert it.
Hope that helps.

Related

Persistent connection string errors in .NET Core DB2 connector

I am migrating a full framework application to .NET Core. Under the full framework, it used the following connection string with the IBM .NET Connector for DB2:
"Server=localhost:50000;Database=testdb;"
The code then assigned UserID and Password properties from credentials vault.
Now, under Core, with the IBM .NET Core connector for DB2 specifically v.2.0.0.100 (long-term support, according to IBM), this connection string throws an exception when a connection string builder is created from it:
{System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.ReliableEnter(Object obj, Boolean& lockTaken)
at IBM.Data.DB2.Core.DB2ConnPool.ReplaceConnStrPwd(String value, String newvalue, Boolean onlyPwd)
at IBM.Data.DB2.Core.DB2Connection.RemoveConnectionStringPassword(String value, Boolean bMask)
at IBM.Data.DB2.Core.DB2ConnectionStringBuilder..ctor(String connectionString)
There is no InnerException. I presume that some mandatory parameters of the connection string that I am not aware of have to be populated under Core, whereas under full framework they were optional. A careful read of IBM documents on DB2 connector Core yielded no mentions of connection string changes, unless I missed them. This blog post mentioned no such breaking changes.
Is anyone aware of mandatory connection string parameters that are missing from my connection string specifically for .NET Core connector?
UPDATE:
If I build the connection string manually, by concatenating the original one above with UserID=MyUser;Password=MyPWD;, and open a connection to the database, then DB2ConnectionStringBuilder works even with the original one above. I cannot wrap my head around it! This makes zero sense. The whole purpose of a connection string builder is to build connection strings from parameters, in a strongly-typed manner. Any ideas?
If you use & IBM.Data.DB2.Core (3.1) nuget package with a connection like:
server=my server;database=myDatabase;user id=myUser;password=MyPassword
Then OdbcConnectionStringBuilder will be enough (e.g.):
var db2Connection = "server=my server;database=myDatabase;user id=myUser;password=MyPassword";
var connectionBuilder = new OdbcConnectionStringBuilder(connection);
var Db2Connection = new Db2Connection(builder.ToString());
or something like this:
var password = builder["password"]?.ToString();
It works fine for me

MVC4 + EntityFramework: metadata only found from within web.config - why is that?

I want to change our EF-driven database-first ASP.NET MVC4 web application in such a way that I can specify the database to connect to at runtime. For the beginning, I simply wanted to substitute the entry in the connectionStrings section of the web.config with a coded version. But first things first. Here's my web.config connection section:
<connectionStrings>
<add name="WEB_Entities" connectionString="metadata=~/bin/Models\WEB_Models.csdl|~/bin/Models\WEB_Models.ssdl|~/bin/Models\WEB_Models.msl;provider=System.Data.SqlClient;provider connection string="data source=testsvr;initial catalog=DEMO;persist security info=True;user id=sa;password=xxxxxxxx;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
When using this setting, everything runs just fine. Now I tried to comment out this entry and hard-code it, thereby moving the setting from the web.config to the global.asax.cs.
I read about the EntityConnectionStringBuilder, but for the beginning I simply want to give the whole connection string as the constructor parameter of an EntityConnection:
string CS =
#"metadata=~/bin/Models\WEB_Models.csdl|
~/bin/Models\WEB_Models.ssdl|
~/bin/Models\WEB_Models.msl;
provider=System.Data.SqlClient;
provider connection string=""Data Source=testsvr\sqlexpress;
Initial Catalog=DEMO;
Integrated Security=True;MultipleActiveResultSets=True""";
conn = new EntityConnection(CS);
conn.Open();
The conn object is a static object that lives in my application class:
public static EntityConnection conn;
In order to use this connection object, I changed my DBContext code to use the aforementioned connection object as constructor parameter, rather than the Name of an entry in the web.config:
public partial class WEB_Entities : DbContext
{
public WEB_Entities()
: base(PAMVCTEST.MvcApplication.conn,true)
//: base("name=WEB_Entities")
{
}
Now when I compile an run the whole thing, the connection to the db server seems to be possible (because I get some network related errors when e.g. changing the datasource to something wrong), but the application does not find the given metadata files. This is the error:
The supplied connection string is not valid, because it contains insufficient mapping or metadata information. Parameter name: connection
I don't understand why the metadata files cannot be found, they are definitely present in the given location. As soon as I change everything back to using the web.config connection entry, everything works as expected.
I also tried changing the metadata files location to this:
res://*/Models.WEB_Models.csdl|res://*/Models.WEB_Models.ssdl|res://*/Models.WEB_Models.msl
I made sure that the resource names are correct with ILMerge. THe result is the same: when I use the web.config way, it works - when I set it by code, I get the same error as mentioned above.
What can I do to resolve this issue? Are there any workarounds? And why in the world do we have to cope with such awful and error-prone connection strings with nested escapings and stuff? It's 2013!!! :-]
Thanks for your help!
Call it from DbContext. Change your DbContext constructor to the following:
public class MyDbContext : DbContext
{
public MyDbContext()
: base("DefaultConnection")
{
}
public MyDbContext(string conStr)
: base(conStr)
{
}
// ...
}
Then add your desired ConStrs to the web config. Finally, when you want another ConStr than the DefaultConnection pass its name to the DbContext() constructor:
Models.MyDbContext db = new Models.MyDbContext("MyConStr");

CreateDataSource not working with WCF and a stored procedure

I am trying to override the connection string in a WCF DataService with CreateDataSource. This seems to work fine for simple requests for tables, but when I try to use a stored procedure it fails...it's always trying to use the original datasource set up for the SP when i worked with the .edmx file.
My code is:
string mConnectionString;
mConnectionString = ConfgurationManager.ConnectionStrings["D7SG_DEVEntities"].ConnectionString;
return new SGEntities(mConnectionString);`
The error which occurs is
The specified named connection is either not found in the
configuration, not intended to be used with the EntityClient provider,
or not valid.
Anyone know how to make this work with a stored procedure ? (This procedure works fine when used with the hard-coded string in web.config)
In your service operation, you're creating the SGEntities without passing in the modified connection string. So it will pick the default connection string from your configuration. In order for this to work you can use this.CurrentDataSource instead of creating a new instance of the SGEntities in your service operation implementation. That will go through the CreateDataSource and should pick up the new connection string.

EntityFramework, AppHarbor and configuration variables

I'm having some trouble with EntityFramework (database first) and AppHarbor.
I'm trying to use the configuration string as inserted into the web.config by AppHarbor (I've added the metadata into the Sequelizer config option on the website), and I'm trying to add some additional values using code provided.
Currently I'm being a very bad person and embedding the string directly into my apps configuration provider - not good if the hosting provider switch DBs on us, so I'm looking to do it the proper way and use the values AppHarbor supply via the web.config.
This is the string as per provided by AppHarbor (passwords and server details removed):
metadata='res://*/MyDataEntities.csdl|res://*/MyDataEntities.ssdl|res://*/MyDataEntities.msl;';provider=System.Data.SqlClient;provider connection string='Server=servername.sequelizer.com;Database=databasename;User ID=username;Password=<snip>;'
If used "as is", that generates the following error:
The specified metadata path is not valid. A valid path must be either an existing directory, an existing file with extension '.csdl', '.ssdl', or '.msl', or a URI that identifies an embedded resource.
I then use the following code (purloined off of one of the AppHarbor support discussions) to append the required extra things EF needs...
if (String.IsNullOrWhiteSpace(ProductionDatabaseConnectionString))
{
// Get it on first read and cache it
var configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var connectionString = configuration.ConnectionStrings.ConnectionStrings["SQLAppHarbor001"].ConnectionString;
// Add the required extra metadata for EF4.x
if (!connectionString.Contains("MultipleActiveResultSets=True;"))
connectionString += "MultipleActiveResultSets=True;";
if (!connectionString.Contains("App=EntityFramework;"))
connectionString += "App=EntityFramework;";
configuration.ConnectionStrings.ConnectionStrings["SQLAppHarbor001"].ConnectionString = connectionString;
configuration.Save();
ProductionDatabaseConnectionString = connectionString;
}
return ProductionDatabaseConnectionString;
That produces the connection string as follows:
metadata='res://*/MyDataEntities.csdl|res://*/MyDataEntities.ssdl|res://*/MyDataEntities.msl;';provider=System.Data.SqlClient;provider connection string='Server=servername.sequelizer.com;Database=databasename;User ID=username;Password=<snip>;'MultipleActiveResultSets=True;App=EntityFramework;
But that produces the error:
Format of the initialization string does not conform to specification starting at index 165.
Index 165 being the start of "provider connection string".
The working connection string I use embedded, which currently works without issue, is:
metadata='res://*/MyDataEntities.csdl|res://*/MyDataEntities.ssdl|res://*/MyDataEntities.msl;';provider=System.Data.SqlClient;provider connection string='Server=servername.sequelizer.com;Database=databasename;User ID=username;Password=<snip>;multipleactiveresultsets=True;App=EntityFramework'
The only real differences being that the "multipleactiveresultsets=True;App=EntityFramework" entries are inside the "provider connection string" string rather than outside.
Other people seem to be using EntityFramework on AppHarbor using the supplied configuration variables fine, so what an I doing wrong?
Update: Multiple Active Result Sets (MARS) can now be enabled for the injected connection string by using the Sequelizer admin panel. This is the recommended approach since the web.config no longer needs to be modified, which causes an AppDomain reload during startup
I came up against this today! I did the following:
var configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var connectionString = configuration.ConnectionStrings.ConnectionStrings["ConnStringAlias"].ConnectionString;
if (!connectionString.Contains("multipleactiveresultsets=True;"))
{
connectionString = connectionString.TrimEnd('\'');
connectionString = connectionString += "multipleactiveresultsets=True;\'";
configuration.ConnectionStrings.ConnectionStrings["ConnStringAlias"].ConnectionString = connectionString;
configuration.Save();
}
The MultipleActiveResultSets property must be inside the provider connection string, which is why you received an error regarding the format of your connection string.
I seen a few 'solutions' around but none seemed to work for me, including the solution at the bottom of a support page of how to do exactly this on AppHarbor's site. The solution provided even sends the application into an infinite loop as the application will restart every time the web.config file is saved, which is every time in the example.

Why connectionstring without metadata works?

I had connection strings for entity framework edmx, which is usual EF connection string with metadata.
Now i am implementing mvc-mini-profiler and wrote method below to create context. I am using just sql connection string part now, no longer using EF connection string.
Now it works but i am curious how it is getting metadata(.csdl, .ssdl address), If it can find now then why 'new Context()' need metadata
public static T GetProfiledContext<T>() where T : ObjectContext
{
// create connection
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString);
// wrap the connection with a profiling connection that tracks timings
var profiledDbConnection = MvcMiniProfiler.Data.ProfiledDbConnection.Get(sqlConnection, MiniProfiler.Current);
// create context
return profiledDbConnection.CreateObjectContext<T>();
}
The reason why it works without metadata is that CreateObjectContext extension method will add these metadata when creating the context. It uses wildcards: res://*/ to get metadata. You can check the implementation here.