Persistent connection string errors in .NET Core DB2 connector - db2

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

Related

No data available with Entity Framework when consumed by second project

I have a web app that uses EF5 to map to a SQL database. This is the standard membership database with some additional tables I've added. Works like a champ in that project.
I have a second project, a windows service running TCP a server, which needs to insert items into the same database. So I reference the web app from this second project and can see my DbContext and entity types as needed.
At runtime, however, none of my DbSets gets populated with data. I have tried explicitly opening the connection to execute queries too, like this:
public MyContext()
: base("DefaultConnection")
{
try
{
Database.Connection.Open();
var command = new System.Data.SqlClient.SqlCommand("SELECT * FROM dbo.Trackers", (SqlConnection) Database.Connection);
var reader = command.ExecuteReader();
bool result = reader.Read();
}
catch (Exception exception)
{
//handle exception
}
this.Database.Connection.Close();
}
The result is false, but the connection is created and the reader is aware that I have four fields in my table. Is anyone aware of a reason this should work in my web app but not in a referencing app?
I had forgotten that the connection string from the web.config file is only honored when running as a web app. The TCP service .exe needs its own copy of the connection string in App.config. It just happened that the default (implicit) connection string on my TCP service connected to a valid, but empty, copy of my database.

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.

Entity Framework - supply a connection string to constructor

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.

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.