I'm using Oracle DataAccess (the last one), I have many Database users inside it. So, I want to create the connectionString in my client application (WPF) and pass it to the server. I'm doing the following in my server:
protected override POSContext CreateDataSource()
{
HttpRequest req = HttpContext.Current.Request;
if (req.Headers != null && Array.FindIndex(req.Headers.AllKeys, c=>c.Equals("db", StringComparison.OrdinalIgnoreCase)) > 0)
{
string database = req.Headers["db"];
string user = req.Headers["user"];
string pass = req.Headers["pass"];
StringBuilder conexion = new StringBuilder();
conexion.Append("DATA SOURCE=");
conexion.Append(database);
conexion.Append(";USER ID=");
conexion.Append(user);
conexion.Append(";PASSWORD=");
conexion.Append(pass);
if (!string.IsNullOrEmpty(conexion.ToString()))
return new POSContext(conexion.ToString());
}
return null;//new POSContext();
}
In the client, I have the following code:
var context = new POS.DataServices.POSContext(new Uri(Storage.Current.UrlService)); // Something like this http://localhost/POService.svc
context.BuildingRequest += (s, args) => //Or SendingRequest2 produces the same result
{
args.Headers.Add("db", Storage.Current.Configuraciones.DB);
args.Headers.Add("user", Storage.Current.Configuraciones.UserName);
args.Headers.Add("pass", Storage.Current.Configuraciones.Password);
};
MessageBox.Show(proxy.POS_CIUDAD.ToList().FirstOrDefault().CIU_DESC); //Alert the city name
When I debug my application I have an error (request error). My client code is trying to Show the message first and then go to the BuildingRequest. How can I pass the connectionString before Entity calls?
CreateDataSource is called once when the service starts, that's why you get the error there.
I think you can try the following:
Add a connection string a property on POSContext, and give POSContext a parameterless constructor. That is the connection string could be set later. And maybe you need to make sure the connection is only set when the connection string is ready.
Override OnStartProcessingRequest method, and change the connection string on CurrentDataSource (POSContext class)
Related
I have a test project, i need to get the connection string value in a test class
public class EmplyeesScenarios
{
private readonly TestServer _testServer;
private readonly AppDbContext _testService;
public EmplyeesScenarios()
{
_testServer = TestServerFactory.CreateServer<TestsStartup>();
var dbOption = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer("//here i need to put the same connection string of _testServer")
.Options;
_testService = new AppDbContext(dbOption);
}
}
You can use the service collection on your test host. For example:
var config = _testServer.Host.Services.GetRequiredService<IConfigurationRoot>();
var connectionString = config.GetConnectionString("Foo");
However, you should actually be using this for all your services, so instead of trying to new up your context at all, just do:
var context = _testServer.Host.Services.GetRequiredService<AppDbContext>();
No connection string needed.
Assuming that your test server is set up properly, you should just resolve your database context from the server instance directly. Like this:
_testServer = TestServerFactory.CreateServer<TestsStartup>();
// create service scope and retrieve database context
using (var scope = _testServer.Host.Services.CreateScope())
{
var db = scope.ServiceProvider.GetService<AppDbContext>();
// ensure that the db is created for example
await db.Database.EnsureCreatedAsync();
// add test fixtures or whatever
}
Technically, you could also resolve the configuration from the test host and the read the connection string out of it but since you are doing an integration test, you should actually test the full integration and not deviate from the existing setup by creating your database context manually.
How can I set the value of connectionTimeout (not commandTimeout) of context by coding (not in connection string) ?
This is readonly:
DbContext.Database.Connection.ConnectionTimeout = 10;
Thanks
Udpate:
My probleme is to test if my context is available quickly and the default time is to long ?
I tried this :
int? oldTimeOut = RepositoryDbContext.Database.CommandTimeout;
try
{
RepositoryDbContext.Database.Connection.ConnectionTimeout = 10; //readonly
RepositoryDbContext.Database.CommandTimeout = 10; // doesn't work, the value stay the same
RepositoryDbContext.Database.Connection.Open();
RepositoryDbContext.Database.Connection.Close();
return true;
}
catch
{
return false;
}
finally
{
RepositoryDbContext.Database.CommandTimeout = oldTimeOut;
}
But I can't change the connectionTimeout is readOnly and the commandTimeout doesn't set the value...
I'm not aware of any option to change the connection timeout via the EF API. You can however change the connection string at runtime. You could parse the configured connection string and change the timeout parameter, or add one if it does not exist.
If you are working with SQL Server only, I would suggest using SqlConnectionStringBuilder. Use the constructor that takes an existing connection string, change the ConnectTimeout, get the modified connection string, and use that connection string when constructing your DbContext.
Depending on what you are actually trying to solve, one alternative might be to use SlowCheetah to easily generate the web.config, with different configuration strings, for different build types.
http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5
UPDATE
Based on your comment...
Try something like
string normalConnectionString = RepositoryDbContext.Database.Connection.ConnectionString;
var connectionBuilder = new SqlConnectionStringBuilder(normalConnectionString);
connectionBuilder.ConnectTimeout = 10;
string testConnectionString = connectionBuilder.ConnectionString;
using (var testRepositoryDbContext = new RepositoryDbContextType(testConnectionString))
{
try
{
testRepositoryDbContext.Database.Connection.Open();
testRepositoryDbContext.Database.Connection.Close();
return true;
}
catch
{
return false;
}
}
Like the title. How can I do it?
I tried something, but it doesn't work like I was expecting.
I'm using an Entity Framework model. I need to pass my connection string like parameter, so, in another file, I've written
namespace MyNamespace.Model
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class MyEntities: DbContext
{
public MyEntities(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
}
}
When I startup the app, I call this constructor in this way, so I can refer to this from anyway in the app:
public static MyEntities dbContext = new MyEntities(mdlImpostazioni.SetConnectionString());
where mdlImpostazioni.SetConnectionString() returns a string (the data are correct):
server=192.168.1.100\SVILUPPO;database=MyDB;uid=myName;pwd=111111;
When I execute this code, it seems to be all ok, but when I try to make a query like:
var query = (from r in MainWindow.dbContext.TabTipoSistema select r);
it throws an exception from here:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException(); //exception here
}
So, this is a bad way... which is the right one? (using only code C#, not from xaml)
Your approach is correct, but you need to remember the connection string for EF requires the metadata and so on. So use the EntityConnectionStringBuilder. For example:
// the model name in the app.config connection string (any model name - Model1?)
private static string GetConnectionString(string model, YourSettings settings)
{
// Build the provider connection string with configurable settings
var providerSB = new SqlConnectionStringBuilder
{
// You can also pass the sql connection string as a parameter instead of settings
InitialCatalog = settings.InitialCatalog,
DataSource = settings.DataSource,
UserID = settings.User,
Password = settings.Password
};
var efConnection = new EntityConnectionStringBuilder();
// or the config file based connection without provider connection string
// var efConnection = new EntityConnectionStringBuilder(#"metadata=res://*/model1.csdl|res://*/model1.ssdl|res://*/model1.msl;provider=System.Data.SqlClient;");
efConnection.Provider = "System.Data.SqlClient";
efConnection.ProviderConnectionString = providerSB.ConnectionString;
// based on whether you choose to supply the app.config connection string to the constructor
efConnection.Metadata = string.Format("res://*/Model.{0}.csdl|res://*/Model.{0}.ssdl|res://*/Model.{0}.msl", model); ;
return efConnection.ToString();
}
// Or just pass the connection string
private static string GetConnectionString(string model, string providerConnectionString)
{
var efConnection = new EntityConnectionStringBuilder();
// or the config file based connection without provider connection string
// var efConnection = new EntityConnectionStringBuilder(#"metadata=res://*/model1.csdl|res://*/model1.ssdl|res://*/model1.msl;provider=System.Data.SqlClient;");
efConnection.Provider = "System.Data.SqlClient";
efConnection.ProviderConnectionString = providerConnectionString;
// based on whether you choose to supply the app.config connection string to the constructor
efConnection.Metadata = string.Format("res://*/Model.{0}.csdl|res://*/Model.{0}.ssdl|res://*/Model.{0}.msl", model);
// Make sure the "res://*/..." matches what's already in your config file.
return efConnection.ToString();
}
EDIT
The exception you get is because when you pass a pure SQL connection string, it assumes you are working with Code first, so it calls the OnModelCreation event. When you include the MetaData section as shown above, that tells EF it's a complete EF connection string.
I believe the problem lies on the Datasource you specify. You need to add the port of the connection, e.g if your SQL Server is configured on Port 1433, try:
server=192.168.1.100,1433\SVILUPPO;database=MyDB;uid=myName;pwd=111111;
more details about connection strings you can find Here
Also I am not sure if uid and pwd are valid, better try User ID and Password:
Server=192.168.1.100,1433\SVILUPPO;Database=MyDB;User ID=myName;Password=111111;
Finally mind the case sensitivity.
Trying to unit test functions which access a entity framework. So i tried to put all the entity code into the test function below? However it stops at the Linq statement; obviously trying to access the database is too much drama for it. Maybe a work around would be too to create a replica database within the unit test function based on sql lite or compact;(Its not a big database anyways) then execution would not have to leave the test function? Is this possible and how would i implement it?
public void RetreiveKeyFnTest()
{
StegApp target = new StegApp(); // TODO: Initialize to an appropriate value
string username = "david"; // TODO: Initialize to an appropriate value
string password = "david1"; // TODO: Initialize to an appropriate value
string ConnectionString = ConfigurationManager.ConnectionStrings["DatabaseEntities"].ToString();
var dataContext = new DatabaseEntities(ConnectionString);
var user = dataContext.Users.FirstOrDefault(u => u.Username.Equals(username) && u.Password.Equals(password));
Assert.IsNotNull(user);
//target.RetreiveKeyFn(username, password);
//Assert.IsInstanceOfType(target.RetreiveLogs,typeof(DataAccess));
//Assert.IsInstanceOfType(target.p);
//Assert.IsNotNull(target.RetreiveLogs.AuthenitcateCredentials(username,password));
//Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
Below is the code i am trying to test:
public void RetreiveKeyFn(string username, string password)
{
BusinessObjects.User p = RetreiveLogs.AuthenitcateCredentials(username,password);
if (p != null)
{
if (RetreiveLogs.RetreiveMessages(p.UserId) == null)
{
DisplayLogs.Text = "Sorry No messages for you recorded in Database, your correspondant might have chose not to record the entry";
}
else
{
MessageBox.Show("LogId = " + RetreiveLogs.RetreiveMessages(p.UserId).LogId + "\n" +
"UserId = " + RetreiveLogs.RetreiveMessages(p.UserId).UserId + "\n" +
"Message Key = " + RetreiveLogs.RetreiveMessages(p.UserId).MessageKey + "\n" + "PictureId = " + RetreiveLogs.RetreiveMessages(p.UserId).PictureId +
" Date & time = " + RetreiveLogs.RetreiveMessages(p.UserId).SentDateTime);
DisplayLogs.Visible = true;
}
}
else
{
MessageBox.Show("Please enter your correct username and password in order to retreive either key, image or both from Databse");
}
}
First, you should be able to access the same database in your test application as the one you're using in your main/actual application. You just need to make sure that your Test project contains your connection string in its own App.config.
The initialization of the context should be done either inside your StegApp(), or you should be able to pass a context to your StegApp() from a different scope. From what I read of your code, your StegApp() will not be able to access the dataContext variable you created.
Your test for null user already happens inside the RetrieveKeyFn() under the AuthenticateCredentials() method so there's no need for the first "Assert.IsNotNull(user)". I would recommend separating your business logic for RetrieveKeyFn from your UI behaviors so that you can easily do unit tests. You can bind the "Messagebox" operations to say a button click event handler which calls just RetrieveKeyFn(). I would suggest maybe something like this:
public class StegApp
{
public DatabaseEntities context;
//other properties
public StegApp()
{
//assuming your DatabaseEntities class inherits from DbContext.
//You should create other constructors that allow you to set options
//like lazy loading and mappings
this.context = new DatabaseEntities();
}
//ASSUMING YOUR RetrieveLogs.RetrieveMessages() function returns
//a Message object. replace this type with whatever type the
//RetrieveLogs.RetrieveMessages() method returns.
public Message RetrieveKeyFn (string username, string password)
{
BusinessObjects.User p = RetreiveLogs.AuthenitcateCredentials(username,password);
if (p != null)
{
var message = RetrieveLogs.RetrieveMessages(p.UserId);
if (message == null)
// handle behavior for no messages. In this case
// I will just create a new Message object with a -1 LogId
return new Message {LogId =-1};
else
return message;
}
else
//handle behavior when the user is not authenticated.
//In this case I throw an exception
throw new Exception();
}
//on your button click handler, do something like:
// try
// {
// var message = RetrieveKeyFn(txtUsername.Text.Trim(), txtPassword.Text.Trim());
// if (message.LogId == -1)
// DisplayLogs.Text = "Sorry No messages for you recorded in Database, your correspondant might have chose not to record the entry";
// else
// {
// MessageBox.Show("Log Id = " + message.LogId)
// etc. etc. etc.
// }
// }
// catch
// {
// MessageBox.Show ("user is not authenticated");
// }
}
When you do your unit test, remember to have the appropriate configuration strings in your test project's App.Config If the app.config does not yet exist, go ahead and create one. You should create tests for all possibilities (i.e. 1) user is valid, you get the message, 2) user is valid, there are no messages, 3) user is invalid).
Here's an example for case 2
[TestMethod]
public void RetrieveKeyFnTest1()
{
StegApp target = new StegApp(); // this creates your context. I'm assuming it also creates your RetrieveLogs object, etc
var username = "UserWithNotMessages"; //this user should exist in your database but should not have any messages. You could insert this user as part of your TestInitialize method
var password = "UserWithNotMessagesPassword"; //this should be the proper password
var message = target.RetrieveKeyFn(username, password);
Assert.AreEqual (-1, message.LogId);
}
I got my unit tests to work fine. The mistake i had was not to copy the app.config file into the test project! Although to be honest i expected Visual studio would have done that anyways.
I've been following this tutorial on how to create an OData service.
http://www.hanselman.com/blog/CreatingAnODataAPIForStackOverflowIncludingXMLAndJSONIn30Minutes.aspx
And it works flawlessly ... but, in the Entity Data Model Wizard, when it asks you to "Choose Your Data Connection" it gives you this warning.
"This connection string appears to contain sensitive data (for example, a password) that is required to connect to the database. Storing sensitive data in the connection string can be a security risk. Do you want to include this sensitive data in the connection string?"
If I choose: "No, exclude sensitive data from the connection string. I will set it in my application code."
I do not see where I can, "in my application code" insert the password. (My company stores them encrypted in the registry)
Plus, I have multiple DBs that I need to connect to, depending on the environment (Dev, CA, or Prod) and I need to know what DB is referenced in the connection string to get the correct password.
Thanks.
When you create your context, you can set a connection string. To build this connection string, you can parse the connection string without the password with an EntityConnectionStringBuilder and then parse the inner connection string with an other ConnectionStringBuilder, depending on your browser. Then you can set the password and pass it to the constructor.
var originalConnectionString = ConfigurationManager.ConnectionStrings["your_connection_string"].ConnectionString;
var entityBuilder = new EntityConnectionStringBuilder(originalConnectionString);
var factory = DbProviderFactories.GetFactory(entityBuilder.Provider);
var providerBuilder = factory.CreateConnectionStringBuilder();
providerBuilder.ConnectionString = entityBuilder.ProviderConnectionString;
providerBuilder.Add("Password", "Password123");
entityBuilder.ProviderConnectionString = providerBuilder.ToString();
using (var context = new YourContext(entityBuilder.ToString()))
{
// TODO
}
I added a "dummy" password in the configuration file ("XXXXX"), then replaced that value with the real password in the entity constructor
public MyDatabaseContainer() : base("name=MyDatabaseContainer")
{
Database.Connection.ConnectionString = Database.Connection.ConnectionString.Replace("XXXXX","realpwd");
}
Modify the constructor of the entities
public sampleDBEntities() : base("name=sampleDBEntities")
{
this.Database.Connection.ConnectionString = #"Data Source=.\;Initial Catalog=sampleDB;Persist Security Info=True;User ID=sa;Password=Password123"; ;
}
My sample application was written in "Database First" mode and the "CreateNewConnectionString" method below works just fine (though it doesn't look all that elegant.)
The "CreateNewConnectionString2" method looks really elegant, BUT causes an exception telling me it's only valid in "Code First" mode.
So I'm providing both methods along with the constructor I modified to use my methods. NOTE AND BEWARE, I've modified code generated by a template and that is subject to being overwritten if the code is regenerated. To me it seems like the right place to put it.
If your application was generated in "Code First" mode, you may need to use "CreateNewConnectionString2" (I have not yet tested this option.)
I hasten to admit that I copied both code blocs from other postings as I don't yet know nearly enough about all this to write my own code.
private static string CreateNewConnectionString(string connectionName, string password)
{
var config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~").ConnectionStrings.ConnectionStrings[connectionName];
//or:
//var config = ConfigurationManager.ConnectionStrings[connectionName];
var split = config.ConnectionString.Split(Convert.ToChar(";"));
var sb = new System.Text.StringBuilder();
for (var i = 0; i <= (split.Length - 1); i++)
{
if (split[i].ToLower().Contains("user id"))
{
split[i] += ";Password=" + password;
}
if (i < (split.Length - 1))
{
sb.AppendFormat("{0};", split[i]);
}
else
{
sb.Append(split[i]);
}
}
return sb.ToString();
}
private static string CreateNewConnectionString2(string connectionName, string password)
{
var originalConnectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
var entityBuilder = new EntityConnectionStringBuilder(originalConnectionString);
var factory = DbProviderFactories.GetFactory(entityBuilder.Provider);
var providerBuilder = factory.CreateConnectionStringBuilder();
providerBuilder.ConnectionString = entityBuilder.ProviderConnectionString;
providerBuilder.Add("Password", password);
entityBuilder.ProviderConnectionString = providerBuilder.ToString();
return entityBuilder.ProviderConnectionString;
}
public ChineseStudyEntities()
: base(CreateNewConnectionString("ChineseStudyEntities", "put YOUR password here")) // base("name=ChineseStudyEntities")
{
}