For the past few hours I've been looking into this problem, found many similar topics but none of them seems to help.
I have a C# application that uses the Entity Framework 5 and I have a .edmx data model that works fine when using the connection string from the app config file.
Now I would like to change the connection string during runtime, but it throws an exception that says:
Keyword not supported: 'metadata'.
Here is the code that I have:
private string GetNewConnectionString(string server, string database, string username, string password)
{
var sqlBuilder = new SqlConnectionStringBuilder()
{
DataSource = server,
InitialCatalog = database,
UserID = username,
Password = password,
IntegratedSecurity = true,
MultipleActiveResultSets = true
};
var entityBuilder = new EntityConnectionStringBuilder()
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = sqlBuilder.ToString(),
Metadata = "res://*/MyTestModel.MyTestModel.csdl|res://*/MyTestModel.MyTestModel.ssdl|res://*/MyTestModel.MyTestModel.msl"
};
return entityBuilder.ToString();
}
public void insertInDB()
{
var newConnectionString = GetNewConnectionString(server, database, username, password);
//newConnectionString = newConnectionString .Replace("\"", """); // doesn't help
//newConnectionString = newConnectionString .Replace("\"", "'"); // doesn't help either
using (var context = new MyTestModel.MyEntities(newConnectionString)) // it crashes here
{
}
}
The metadata should be correct, because I have copied it from the app config file, also if I copy the value of newConnectionString to the app config and use it, it works fine if I replace the quotations with ".
This is the value of newConnectionString (i've only replaced the credentials with some dummy credentials):
metadata=res:///MyTestModel.MyTestModel.csdl|res:///MyTestModel.MyTestModel.ssdl|res://*/MyTestModel.MyTestModel.msl;provider=System.Data.SqlClient;provider
connection string="Data Source=myServer;Initial
Catalog=myDatabase;Integrated Security=True;User
ID=myDbUser;Password=myDbUserPassword;MultipleActiveResultSets=True"
I cannot see what is wrong with it, can anybody spot something?
This answer: Entity Framework change connection at runtime solves the problem
I cannot understand what the problem with my code was, but it works fine with the extension method from the link.
Related
I have two questions regarding connecting to an Oracle database using C#.NET.
My program references Oracle.DataAccess.dll (version 2.122.19.1) in my Oracle 19c client 64-bit folder.
I have a using statement:
using Oracle.DataAccess.Client;
and this code to connect:
string connectionString = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=myserver.sys.mycompany.com)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=mydb.mycompany.com)));User Id=MyLanId;Password=MyPasswors;Validate Connection=True;"
using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
}
Question #1:
How do I create an OID connection string that does not specify the DB name but instead queries the LDAP for the detail server/port info that is hardcoded in my TNS-less connection string above?
For example, in a Java program, we connect like this.
base.oracle.db.url=jdbc:oracle:thin:#ldap://oid.gtm.internal.mycompany.com:3060/dbname,cn=OracleContext,dc=mycompany,dc=com
What would the .NET equivalent be?
Question Number #2:
When I compile my C# program I selected "Any CPU" yet my program only works when I reference the 64-bit Oracle.DataAccess.dll. Is this because my PATh variable mentions my 64-bit oracle client folder before my 32-bit folder (I have both)? Right now, my PATH variable is way over the max length of 2048 and I cannot change it without making it shorter and I'm not sure what to delete.
I found this SO post,
How do I query LDAP from C# to resolve Oracle TNS hostname while using managed ODP.NET?
Instead of just passing in a connection string that points to an LDAP server, I can do an LDAP query to get the db descriptor to build the connection string.
It's a 2 step process, so I probably want to cache the connection string once I build it. I normally count on connection pooling and "close" connections when I am done with them but this two step process seems like I have to manually add some caching of the connection string to avoid the overhead of hitting LDAP multiple times.
Is there a better way?
string directoryServer = "oid.gtm.internal.mycompany.com:3060";
string defaultAdminContext = "cn=OracleContext,dc=mycompany,dc=com";
string serviceName = "mydb";
string userId = "mylanid";
string password = "mypwd";
using (IDbConnection connection = GetConnection(directoryServer, defaultAdminContext, serviceName, userId, password))
{
connection.Open();
connection.Close();
}
private static IDbConnection GetConnection(string directoryServer, string defaultAdminContext,
string serviceName, string userId, string password)
{
string descriptor = ConnectionDescriptor(directoryServer, defaultAdminContext, serviceName);
// Connect to Oracle
string connectionString = $"user id={userId};password={password};data source={descriptor}";
OracleConnection con = new OracleConnection(connectionString);
return con;
}
private static string ConnectionDescriptor(string directoryServer, string defaultAdminContext,
string serviceName)
{
string ldapAdress = $"LDAP://{directoryServer}/{defaultAdminContext}";
string query = $"(&(objectclass=orclNetService)(cn={serviceName}))";
string orclnetdescstring = "orclnetdescstring";
DirectoryEntry directoryEntry = new DirectoryEntry(ldapAdress, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, query, new[] { orclnetdescstring },
SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
byte[] value = searchResult.Properties[orclnetdescstring][0] as byte[];
if (value != null)
{
string descriptor = Encoding.Default.GetString(value);
return descriptor;
}
throw new Exception("Error querying LDAP");
}
We have several environments:
Development
Azure
On Prem
The Crystal Report pulls data from a SQL server, but the database is different depending on the environment.
There is a config file in the project called connectionStrings.config and the connection string that we use for the website is stored there. The contents of the file looks like this:
<connectionStrings>
<add name="dataModel" connectionString="data source=...;initial catalog=...;user id=...;password=...;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
To load the report I setup the following class:
public class CrystalReportUtil
{
static string ReportsDirectory = ConfigurationManager.AppSettings["ReportsDirectory"];
private static string ConnectionString = ConfigurationManager.ConnectionStrings["dataModel"].ConnectionString;
public static ReportDocument GetReport(string reportName)
{
var report = new ReportDocument();
var fileDirectory = $"{Utilities.GetProjectRoot()}/Reports";
if (System.Web.HttpContext.Current != null)
{
fileDirectory = System.Web.HttpContext.Current.Server.MapPath(ReportsDirectory);
}
string file = Path.Combine(fileDirectory, $"{reportName}.rpt");
if (!File.Exists(file))
{
throw new System.Exception($"Unable to find report file: {file}");
}
report.Load(file);
var builder = new SqlConnectionStringBuilder(ConnectionString);
var dataSource = builder.DataSource;
var user = builder.UserID;
var password = builder.Password;
var database = builder.InitialCatalog;
RecursivelyRemapConnection(report, user, password, dataSource, database);
report.VerifyDatabase();
return report;
}
private static void RecursivelyRemapConnection(ReportDocument report, string username, string password, string server, string database)
{
foreach (IConnectionInfo connection in report.DataSourceConnections)
{
connection.SetLogon(username, password);
connection.SetConnection(server, database, false);
}
report.SetDatabaseLogon(username, password);
foreach (Table table in report.Database.Tables)
{
table.LogOnInfo.ConnectionInfo.ServerName = server;
table.LogOnInfo.ConnectionInfo.DatabaseName = database;
table.LogOnInfo.ConnectionInfo.UserID = username;
table.LogOnInfo.ConnectionInfo.Password = password;
table.LogOnInfo.ConnectionInfo.IntegratedSecurity = false;
}
if (!report.IsSubreport)
{
foreach (ReportDocument subreport in report.Subreports)
{
RecursivelyRemapConnection(subreport, username, password, server, database);
}
}
}
}
The issue is that in my development environment, it is working just fine whereas in the other environments I get the following exception:
Log on failed. No error.
at CrystalDecisions.ReportAppServer.ClientDoc.ReportClientDocumentClass.VerifyDatabase()
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.VerifyDatabase()
at CrystalDecisions.CrystalReports.Engine.ReportDocument.VerifyDatabase()
I verified that the connection is changing in my development environment by doing the following:
Creating a copy of my datasource
In the designer, setting the datasource to the copied database
Deleting the copy
Previewing the report in the designer fails to load (as expected)
Generating the report using my utility class
The last step successfully generates the report which tells me that the datasource is changing, otherwise I would expect a logon failure.
What makes this particularly frustrating is that some reports in the non-developing environments work fine whereas some are producing the exception. So I don't understand why it works for some, but not for all.
Update
If I change my connection string in my development environment to the connection string used in Azure or in the On-Prem, it still works in my development environment. I really don't believe that the issue is with my connection string, but then again I have no idea what is causing the logon failure at this point.
After several days of looking into this, I finally found out what was causing the issue.
The particular report that was failing was using the MSOLEDBSQL provider instead of SQLOLEDB.
To fix the issue, I:
Opened the Crystal Report in Visual Studio
Right-clicked on the report
Database > Set Datasource Location
Expanded the properties of the current datasource
Double-clicked on the provider
Changed the value from MSOLEDBSQL to SQLOLEDB
Reentered the logon credentials (even though they get changed at runtime)
Saved the report
Build the solution
Republished the project
I am using the following code to connect to sql throgh windows authentication.
string connctionstring = "connectionString={0};Database={1};Integrated Security=SSPI;";
string _connctionstring = string.Format(connctionstring, datasource, initialCatalogue);
SqlConnection _connection = new SqlConnection(_connctionstring);
_connection.Open();
But i am getting the following error. Help please.I am able to login through sql server.
The connection string format is not correct
Change to this:
string connctionstring = "Data Source={0};Database={1};Integrated Security=SSPI;";
Or
string connctionstring = "Server={0};Database={1};Integrated Security=SSPI;";
While Peyman's answer does cover the basic issue (connectionString is not a valid key for the string) a better solution is to use a SqlConnectionStringBuilder, this will also help you do proper escaping if you have odd characters in your string (for example if your database name contained a space)
var scsb = new SqlConnectionStringBuilder();
scsb.DataSource = datasource;
scsb.InitialCatalog = initialCatalogue;
scsb.IntegratedSecurity = true;
//You also really should wrap your connections in using statements too.
using(SqlConnection connection = new SqlConnection(scsb.ConnectionString))
{
connection.Open();
//...
}
OK, I've been working on this since late last night and early this morning. I am trying to insert some data into a SQL Server CE database. I am at the end of my rope.
Here is the code I am trying to implement:
public static void InsertData(string sqlStatement)
{
try
{
//string strConn = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
//string StartupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Remove(0, 6); // remove file:\\
//string connectionString = #"Data Source=Database1.sdf";
//string connectionString = #"Data Source = |DataDirectory|\Database1.sdf";
//string connectionString = #"Data Source = C:\Users\My\Documents\Visual Studio 2012\Projects\VIN_Decoder\VIN Decoder\VIN Decoder\Database1.sdf"
//string connectionString = string.Format("Data Source={0}; Persist Security Info=False;", Path.Combine(Application.StartupPath, "Database1.sdf"));
//THIS ONE ACTUALLY WORKS!!!!!!!!!!!! :
//string connectionString = #"Data Source = C:\Users\My\Documents\Visual Studio 2012\Projects\VIN_Decoder\VIN Decoder\VIN Decoder\Database1.sdf"
string connectionString = Properties.Settings.Default.Database1ConnectionString;
SqlCeConnection sqlceCon = new SqlCeConnection(connectionString);
sqlceCon.Open();
SqlCeCommand sqlCeCom = new SqlCeCommand(sqlStatement, sqlceCon);
sqlCeCom.ExecuteNonQuery();
sqlCeCom.Dispose();
sqlceCon.Close();
}
catch (Exception e)
{
}
}
You can see all of the commented connection strings to see what I have tried. The strange thing is that I can simply use the following connection string in another method (a SELECT statement, not an INSERT INTO statement) that gets a single value from the data and it works fine.
string connectionString = #"Data Source = |DataDirectory|\Database1.sdf";
I have tested the query directly and like the comment says, the static path does work so that tells me my query is good. But of course I need something relative for publishing and multi-developer development.
Most of the connection strings I have tried allow the try block to complete with no errors, but the data doesn't get inserted.
Any suggestions?
I searched my heart out before posting this question, but Simon is right about similarity. However, we're going to have a better answer here. Corak is correct. I set property to not copy, deleted copy in bin\debug folder and app wouldn't run in debug mode because it couldn't find db even though paths all point to other file location. App is looking for copy in bin\debug. Ultimately, it was working all along. I recommend someone to make a connection to the copy in bin\debug so they can test values in the Server Explorer. Thanks for your help Corak and everyone else.
/*If your solution is web application, make sure Database1.sdf location path is under the
* App_Data folder
* Web.Config <add name="LocalDB" connectionString="Data Source=|DataDirectory|\Database1.sdf"/>
*/
protected string ConnString
{
get
{
return ConfigurationManager.ConnectionStrings["LocalDB"].ToString();
}
}
/*
* or you can use try absolute path as connectionstring
* modify your directory path as PhysicalApplicationPath result
*/
public string BaseDirConnString
{
get
{
if ((HttpContext.Current == null) || (HttpContext.Current.Request == null))
{
throw new ApplicationException("...");
}
return #"Data Source=" + HttpContext.Current.Request.PhysicalApplicationPath + "VIN Decoder\\Database1.sdf";
}
}
protected SqlCeConnection SqlCeConnection
{
get
{
var connection = new SqlCeConnection(BaseDirConnString);
connection.Open();
return connection;
}
}
I'd like to supply the connection string for my database at runtime. I am using the Entity Framework. This is what I have so far
class MyClassDBContext:DbContext
{
public MyClassDBContext(string str) : base(str)
{
this.Database.Connection.ConnectionString = str;
}
}
To use the above code, I tried
//create connection string
EntityConnectionStringBuilder myConn = new EntityConnectionStringBuilder();
myConn.Provider = "System.Data.SqlClient";
myConn.ProviderConnectionString = "user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30";
//inject the connection string at runtime
MyClassDBContext a = new MyClassDBContext(myConn.ToString())
The above code gave me an error saying "Provider keyword not supported".
To attempt to debug this error, I tried the following
MyClassDBContext a = new MyClassDBContext("metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string=user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
Now, I got an error saying "metadata keyword not supported". So I changed my code to
MyClassDBContext a = new MyClassDBContext("provider=System.Data.SqlClient;provider connection string=user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
Now I got an error saying "provider keyword not supported". So I again changed my code to
MyClassDBContext a = new MyClassDBContext("user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
and now it works!. My question is : how do I specify the provider and metadata at runtime? It looks like only the connection string is being accepted. I am using Entity 4.3.1 from Nuget.
Thanks
edmx file based EF require the "Provider" and "Metadata" content. Code-first based EF doesn't require this, requiring only the regular connection string. You could use a SqlConnectionStringBuilder (instead of EntityConnectionStringBuilder) to build this normal connection string if you'd like. But as you've seen, you need only specify the actual connection details. The Provider and Metadata aren't needed in EF 4.3.1's DbContext Code-first paradigm.
Building on HatSoft's answer:
var entityConnectionStringBuilder= new EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "System.Data.SqlClient";
entityConnectionStringBuilder.ProviderConnectionString = <your SQL Server connection string>;
entityConnectionStringBuilder.Metadata = "res://*";
MyClassDBContext a = new MyClassDBContext(entityConnectionStringBuilder.ToString());
The EntityConnectionStringBuilder class can be used to to specify provider and metadata at runtime
e.g.
var entityConnectionStringBuilder= new
EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "System.Data.SqlClient";
entityConnectionStringBuilder.Metadata =
"res:///Example.csdl|res:///Example.ssdl|res://*/Example.msl";
Please see for more on CSDL, SSDL & MSDL in Metadata
I followed this link
and also this one
How to use EF Code-First without an app.conf file?
Basically what I do is almost like you, create a constructor with a string and calling the base.
But I also set the provider in this constructor.
here's a example
public Context(string ConnectionString) : base(ConnectionString) {
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("Oracle.DataAccess.Client");
}
That way you can specify the provider. And you won't get the provider keyword error since you don't need to specify in the connection string
Here's how I call it.
var dbCont = new ClassLibrary1.Models.Context("DATA SOURCE=xxx;PASSWORD=xxx;USER ID=xxx");
Hope that helps took me long time to find it
It's old question but maybe it will be useful for someone
var provider = (DbProviderFactory)System.Data.Entity.DbConfiguration
.DependencyResolver
.GetService(typeof(DbProviderFactory), "invariant provider name");
var conn = provider.CreateConnection();
//conn.ConnectionString = "sample connection string";
DbInterception.Dispatch.Connection.SetConnectionString(conn, new DbConnectionPropertyInterceptionContext<string>()
.WithValue("sample connection string"));
return new SampleDbContext(conn,true);