I have a scenario where there are multiple dbs that have the same schema and clients can pick which database to query. Is there a way to include the database name when doing an oData query from silverlight so it can reuse the same services?
Lets say I have this query (see below) being executed at the client (silverlight/wp7), how do I get this query to run against the database that the user picked when they first launched the app?
private DataServiceCollection _employees;
private void LoadEmployees()
{
DataModelContainer dmc = new DataModelContainer(new Uri("http://localhost:63832/DataService.svc"));
var query = (from e in dmc.Employees
where e.StartDate == BaseDate
select e);
_employees = new DataServiceCollection(dmc);
_employees.LoadCompleted += new EventHandler(_employees_LoadCompleted);
_employees.LoadAsync(query);
}
You should be doing that in your configuration files, via connectionstrings element.
eg:
<configuration>
<!-- Other configuration settings -->
<connectionStrings>
<add name="Sales"
providerName="System.Data.SqlClient"
connectionString= "server=myserver;database=Products;uid=<user name>;pwd=<secure password>" />
<add name="NorthWind"
providerName="System.Data.SqlClient"
connectionString="server=.;database=NorthWind;Integrated Security=SSPI" />
</connectionStrings>
To retrieve the connection strings dynamically, based on your query string from the client, make use of the ConfigurationManager class. The following links will help you in this regard:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.aspx
www.dotnet-guide.com/configurationmanager-class.html
Here is what I came up with: The AddQueryOption will add a value to the query string, in my case the key of the db that I want to run the query against.
var query = (from e in dmc.Employees.AddQueryOption("dbKey", SelectedDB)
where e.StartDate == BaseDate select e);
_employees = new DataServiceCollection(dmc);
_employees.LoadCompleted += new EventHandler(_employees_LoadCompleted);
_employees.LoadAsync(query);
}
In the DataService class I overrride the CreateDataSource method and return a DataModelContainer with the correct connection string for the query
protected override DataModelContainer CreateDataSource()
{
DataModelContainer dmc = new DataModelContainer(GetConnectionString());
return dmc;
}
private string GetConnectionString()
{
string dbKey = HttpContext.Current.Request.Params["dbKey"].ToString();
string cnnKey = "DataModelContainer" + dbKey;
return ConfigurationManager.ConnectionStrings[cnnKey].ToString();
}
Related
I'm trying to use SQL for the first time to store data in one of my projects, I'm using this tutorial as it makes the most sense to me, and to be honest I like the guys videos, and hate clicking on hundreds of bad videos before finding one I can learn from.
Anyway I'm making an app where I need to access and save to a SQL database I have made in MSSMS, I have a class library for logic, a data class library for data access, and a WPF interface (I also plan to add an ASP interface with less edit features, but adding Web API, its all for learning)
In the video to connect dapper the guy sets up a helper to get the connection string, but that is looking up an App.Config through configuration manager (which he says is baked in and you just need to add a reference, but it seems now is a NuGet package).
But I have no App.config anywhere, and I've never used that so I don't know if I'm supposed to add it, will it do anything, where would I add it? Or do I do something completely different nowadays using .NET Core not .NET Framework.
Sorry long post and maybe not as clear as it should be but I'm struggling here at the first hurdle and Google seems to be useless on this one.
As a side note I'm also planning to save key value pairs from my objects (as Dictionary<string, string>), am I best just having a new table just for those and store the I'd of the object it was associated with, the key, and the value in their own columns?
Here is a basic dotnet core command line exe
Note, this is a loose mini implementation of what is in this article : https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures
https://github.com/granadacoder/dotnet-core-on-linux-one/tree/master/src/ConsoleOne
typical appsettings.json contents
https://github.com/granadacoder/dotnet-core-on-linux-one/blob/master/src/ConsoleOne/appsettings.json
{
"ConnectionStrings": {
"MyConnectionString": "Data Source=someServer\someInstance,1433;Database=master;User Id=sa;Password=Password1#;"
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AllowedHosts": "*"
}
this top layer has and defines these values. in my sample "ConsoleOne" is the top layer.
I also have a BAL and DAL layers.
https://github.com/granadacoder/dotnet-core-on-linux-one/tree/master/src/Bal
https://github.com/granadacoder/dotnet-core-on-linux-one/tree/master/src/Dal
my example uses Dapper, and it fishes the connection string
https://github.com/granadacoder/dotnet-core-on-linux-one/blob/master/src/Dal/EmployeeDataLayer.cs
public class EmployeeDataLayer : IEmployeeDataLayer
{
private readonly Microsoft.Extensions.Configuration.IConfiguration config;
public EmployeeDataLayer(Microsoft.Extensions.Configuration.IConfiguration config)
{
this.config = config;
}
public IDbConnection Connection
{
get
{
string connectionString = this.config.GetConnectionString("MyConnectionString");
return new SqlConnection(connectionString);
}
}
public async Task<Employee> GetByID(int id)
{
using (IDbConnection conn = this.Connection)
{
string sql = "SELECT ID, FirstName, LastName, DateOfBirth FROM Employee WHERE ID = #ID";
sql = "SELECT TOP 1 id as ID, 'FName' + name as FirstName, 'LName' + name as LastName, crdate as DateOfBirth FROM sysobjects order by id";
conn.Open();
var result = await conn.QueryAsync<Employee>(sql, new { ID = id });
return result.FirstOrDefault();
}
}
public async Task<ICollection<Employee>> GetByDateOfBirth(DateTime dateOfBirth)
{
using (IDbConnection conn = this.Connection)
{
string sql = "SELECT ID, FirstName, LastName, DateOfBirth FROM Employee WHERE DateOfBirth = #DateOfBirth";
sql = "SELECT TOP 3 id as ID, 'FName' + name as FirstName, 'LName' + name as LastName, crdate as DateOfBirth FROM sysobjects order by id";
conn.Open();
var result = await conn.QueryAsync<Employee>(sql, new { DateOfBirth = dateOfBirth });
return result.ToList();
}
}
}
(Note, my DAL code above is not hitting real backend tables, it is a simple demo I did)
But back to the configuration:
I accomplish this by "injecting" the Configuration object into the class (see the constructor)
With dotnet-core, you typically use the built in IoC/DI
You can see that here:
https://github.com/granadacoder/dotnet-core-on-linux-one/blob/master/src/ConsoleOne/Program.cs
private static IServiceProvider BuildDi(IConfiguration config)
{
string connectionString = config.GetConnectionString("MyConnectionString");
return new ServiceCollection()
.AddSingleton<IEmployeeManager, EmployeeManager>()
.AddTransient<IEmployeeDataLayer, EmployeeDataLayer>()
.AddLogging(loggingBuilder =>
{
// configure Logging with NLog
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
loggingBuilder.AddNLog(config);
})
.AddSingleton<IConfiguration>(config)
.BuildServiceProvider();
}
I ended up just going to my UI Add > New Item > Application Configuration File, called App.config and using this code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<clear/>
<add name="Thenameasusedinmyapp" connectionString="Server =.; Database = NameofDatabase; Trusted_Connection = True;" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
Here is what i have so far :
<add name="gymEntities1" connectionString="metadata=res://*/DateModel.csdl|res://*/DateModel.ssdl|res://*/DateModel.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=gym;user id=sa;password=xxxx;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
it works on my LocalHost Database, and I can load my Data from it.
however, I have a server, installed sqlserver with my database on that, basicaly when i change my sqlcommands connecting string this work but in some part of my program I used entity framework and have no idea how to change it connecting string, with some posts in stackoverflow I change that to
<add name="gymEntities2" connectionString="metadata=res://*/DataModel.csdl|res://*/DataModel.ssdl|res://*/DataModel.msl;provider=System.Data.SqlClient;provider connection string="data source=tcp:46.105.124.144;initial catalog = gym ;User ID=sa;Password=xxxx"" providerName="System.Data.EntityClient" />
but it still read datas from my localhost and not connect to my server.
I don't know how when i change this connecting string to my server it still read data from my localhost database.
what is the best way to change connecting string from App.Config?
First Possible Issue:
It is less likely since other people suggested to you.But, it is possible that you are missing a connection string in one of your web.config or app.config.
It is a good habit to copy your string to every project. Example. I have 3 different projects in my solution (Library, WCF, WPF). I copied the following connection string to each project (One sample for Local SQL Server and another for Azure):
<connectionStrings>
<add name="LocalSQLServerSample.CodeREDEntities" connectionString="metadata=res://*/CodeRED.csdl|res://*/CodeRED.ssdl|res://*/CodeRED.msl;provider=System.Data.SqlClient;provider connection string="data source=MachineName\ServerName;initial catalog=CodeRED;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
<add name="AzureSQLServerSample.CodeREDEntities" connectionString="metadata=res://*/CodeRED.csdl|res://*/CodeRED.ssdl|res://*/CodeRED.msl;provider=System.Data.SqlClient;provider connection string='data source=azureservername.database.windows.net;initial catalog="CodeRED";persist security info=True;user id=CodeRED;password=R%Chd$g*VHs28eEr;MultipleActiveResultSets=True;App=EntityFramework'" providerName="System.Data.EntityClient" />
</connectionStrings>
Second possible issue:
You have mentioned that you are using entity framework. Are you using ObjectContext to access it? If yes, I have the method below to call it every time I want to access any database:
From sample above: name="LocalSQLServerSample.CodeREDEntities"
_containerName is CodeREDEntities (The same for all my connections).
environment is to determine which database you are connecting to. For example, in the above connection sample, I have LocalSQLServerSample and AzureSQLServerSample and I usually have something like PRODUCTION, DEVELOPMENT, TESTING....
public static ObjectContext getObjectContext(string environment, bool isReadOnly)
{
environment = environment == null ? "" : environment.Trim();
environment = environment.Length == 0 ? "" : (environment + ".");
ObjectContext objectContext = new ObjectContext(
ConfigurationManager.ConnectionStrings[environment + _containerName].ToString());
objectContext.DefaultContainerName = _containerName;
objectContext.CommandTimeout = 0;
objectContext.ContextOptions.ProxyCreationEnabled = !isReadOnly;
return objectContext;
}
Sample of how to use it:
Common is a common class that I use to store shared information such as getting common error format used for Common.getInnerExceptionMessage.
Also, you don't have to always pass environment, you can store it as a constant to be able to call it such as (I always pass it to be able to mix connection when I need to for specific calls): You can modify connection from anywhere by changing _selectedEnvironment if you do not wish to pass it everywhere.
public const string _ENVIRONMENT_DEVELOPMENT = "LocalSQLServerSample";
public const string _ENVIRONMENT_PRODUCTION = "AzureSQLServerSample";
public static string _selectedEnvironment = _ENVIRONMENT_PRODUCTION;
Sample of getting item based on id:
Note: User is a class generated by entity framework from database.
public UsersDataGrid GetItem(string environment, long id)
{
ObjectContext objectContext = Common.getObjectContext(environment, false);
try
{
var item = objectContext.CreateObjectSet<User>()
.Where(W => W.ID == id)
.Select(S => new UsersDataGrid()
{
Active = S.Active,
ID = S.ID,
Unique_ID = S.Unique_ID,
First_Name = S.First_Name.ToUpper(),
Last_Name = S.Last_Name.ToUpper(),
Email = S.Email,
School = S.School.Title.ToUpper(),
Gender = S.Gender.Title.ToUpper(),
TShirt_Size = S.TShirt_Size.Title.ToUpper(),
GUID = S.GUID + "",
Note = S.Note,
Machine_User = S.Machine_User,
Machine_Name = S.Machine_Name,
Created_On = S.Created_On,
Last_Updated_On = S.Updated_On
}).FirstOrDefault();
return item;
}
catch (Exception exception)
{
return new UsersDataGrid()
{
Note = ("Service Error: " +
Common.getInnerExceptionMessage(exception))
};
}
}
2nd Sample: Updating a user:
Note: Common.CopyValuesFromSourceToDestinationForUpdate is only a generalized method copying items from item object to entityItem, instead you can copy values normally such as entityItem.ID = item.ID and so on...
public Result Update(string environment, User item)
{
ObjectContext objectContext = WCF_Service_Library.Classes.Common.getObjectContext(environment, false);
try
{
var entityItem = objectContext.CreateObjectSet<User>()
.AsEnumerable().Where(Item => Item.ID == item.ID).ToList().FirstOrDefault();
if (entityItem == null)
return new Result("Item does NOT exist in the database!");
entityItem = Common.CopyValuesFromSourceToDestinationForUpdate(item, entityItem) as User;
objectContext.SaveChanges();
return new Result(entityItem.ID);
}
catch (Exception exception)
{
return new Result("Service Error: " + Common.getInnerExceptionMessage(exception));
}
}
Third issue (it does not look like it, but you may encounter it):
If you publish your app and ONLY sign your WPF project, you will not get error during publishing, but you may not be able to connect to the database. You must sign all your projects in your solution.
Hopefully this help you with your problem
Check the WebConfig of your startup project. Entity framework reads ConnnectionString from AppConfig when you run Update Model From Db operation .
But in runtime it reads ConnnectionString from WebConfig in your Startup project
I hope it is use for you
Add this for App.Config files
<connectionStrings>
<add name="Dbconnection"
connectionString="Server=localhost; Database=OnlineShopping;
Integrated Security=True"; providerName="System.Data.SqlClient" />
</connectionStrings>
This connection string must be work:
<add name="Name"
connectionString="metadata=<Conceptual Model>|<Store Model>|<Mapping Model>;
provider=<Underlying Connection Provider>;
provider connection string="<Underlying ConnectionString>""
providerName="System.Data.EntityClient"/>
If you have any problems with writing connection string, you can use following code on the page.
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.
I have created an EDMX using EF 5 with the Model First approach, i.e. I started with a blank designer and modeled my entities. Now I want to be able to use this model defined in the EDMX but supply runtime SQL Server connection strings without modyfing the config file.
I know how to pass a connection string to the DbContext but the issue is locating the metadata for the mappings within the assembly.
For example, my EDMX has this connection string in the app.config
<add name="MesSystemEntities" connectionString="metadata=res://*/Data.DataContext.EntityFramework.MesSystem.csdl|res://*/Data.DataContext.EntityFramework.MesSystem.ssdl|res://*/Data.DataContext.EntityFramework.MesSystem.msl;provider=System.Data.SqlClient;provider connection string="data source=MyMachine;initial catalog=MesSystem;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
The part that I am missing is the "metadata=res://*/Data.DataContext.EntityFramework.MesSystem.csdl|res://*/Data.DataContext.EntityFramework.MesSystem.ssdl|res://*/Data.DataContext.EntityFramework.MesSystem.msl;"
I want to be able to create a DbContext programmatically passing in a SQL Server connection string but "add on" the metadata portion.
This is what I would like to be generated by the T4 file...
public partial class MesSystemEntities : DbContext
{
public MesSystemEntities()
: base("name=MesSystemEntities")
{
}
public MesSystemEntities(string sqlServerConnectionString)
: base(GetEfConnectionString(sqlServerConnectionString))
{
}
private string GetEfConnectionString(string sqlServerConnectionString)
{
// values added by T4 generation
string format = "metadata=res://*/Data.DataContext.EntityFramework.MesSystem.csdl|res://*/Data.DataContext.EntityFramework.MesSystem.ssdl|res://*/Data.DataContext.EntityFramework.MesSystem.msl;;provider=System.Data.SqlClient;provider connection string=\"{0}\"";
return String.Format(format, sqlServerConnectionString);
}
...
}
My question is how can I get the metadata I need in the T4 generation file to create the Entity Framework connection without hardcoding it for each EDMX file
OR
is there an easier way to load the metadata from the assembly programmatically?
I had the same issue, so instead of relying on all the meta data in the connection string (which I think is not a good idea) I wrote a method to create it from a standard connection string. (I should probably refactor it into an Extension method for DbContext, but this should do)
internal static class ContextConnectionStringBuilder
{
// Modified Version of http://stackoverflow.com/a/2294308/209259
public static string GetEntityConnectionString(string ConnectionString,
Type ContextType)
{
string result = string.Empty;
string prefix = ContextType.Namespace
.Replace(ContextType.Assembly.GetName().Name, "");
if (prefix.Length > 0
&& prefix.StartsWith("."))
{
prefix = prefix.Substring(1, prefix.Length - 1);
}
if (prefix.Length > 1
&& !prefix.EndsWith("."))
{
prefix += ".";
}
EntityConnectionStringBuilder csBuilder =
new EntityConnectionStringBuilder();
csBuilder.Provider = "System.Data.SqlClient";
csBuilder.ProviderConnectionString = ConnectionString.ToString();
csBuilder.Metadata = string.Format("res://{0}/{1}.csdl|"
+ "res://{0}/{1}.ssdl|"
+ "res://{0}/{1}.msl"
, ContextType.Assembly.FullName
, prefix + ContextType.Name);
result = csBuilder.ToString();
return result;
}
}
Basic usage is something like:
string connString =
ConfigurationMananager.ConnectionStrings["name"].ConnectionString;
string dbConnectionString = ContextConnectionStringBuilder(connString,
typeof(MyDbContext));
var dbContext = new MyDbContext(dbConnectionString);
I have an application that I want to be able to configure the connection string for my LINQ to SQL. I've tried so many different ways but cannot seem to get it working. I want to do this dynamically in the code when the app is run, the reason for this is that the user can change the connection settings.
If I delete the connectionString out of app.config the application still works OK (communicating) which makes me wonder where I should be changing the connection string?
I think that the best way to do it is a combination of Albin's and Rup's answers. Have a value in the config file, and then read it at run time and feed it to the context constructor, something like this:
WEB.CONFIG:
<appSettings>
<add key="ConString" Value="The connection string" />
CODE:
//read value from config
var DBConnString = System.Configuration.ConfigurationManager.AppSettings("ConString");
//open connection
var dataContext= new MyDataContext(sDBConnString)
this way you can change the connection string even at runtime and it will work and change on the running program.
You can pass an override connection string into the DataContext constructor:
var db = new MyDataContext("Data Source=Something Else;")
The DBML class (YourDataContext) has an overloaded constructor which takes ConnectionString, so try instantiating that instead of the default one.Get the connection string from app.config and use that to create the instance.
YourDataContext context = new YourDataContext (ConfigurationManager.ConnectionStrings["ConnStringInAppConfig"].ConnectionString)
You should change it in app.config. The reason it works without is that the LINQ2SQL designer creates a fallback to the connection string used when designing the DBML. If you define a connection string in app.config that is used instead.
By Default your constructor look like this
public dbDataContext() :
base(global::invdb.Properties.Settings.Default.Enventory_4_0ConnectionString, mappingSource)
{
OnCreated();
}
You can change return value Instead of
//Original
public string Enventory_4_0ConnectionString {
get {
return ((string)(this["Enventory_4_0ConnectionString"]));
}
}
this
//Modified code
public string Enventory_4_0ConnectionString {
get {
return (System.Configuration.ConfigurationManager.ConnectionStrings["Enventory_4_0ConnectionString"].ConnectionString);
}
}
Inside your dbml file designer.cs add this dynamic call to base class constructor. It will work for local, dev and prod automatically pulling from current web.config without need to pass connection every time;
public HallLockerDataContext() :
base(ConfigurationManager.ConnectionStrings["MYDB1"].ConnectionString, mappingSource)
{
OnCreated();
}
Usage:
using (var db = new HallLockerDataContext())
{
}