I am developing an application with WPF and Entity Framework CodeFirst.
One requirement is that the user could create separate database file per new project.
DbContext :
public class TestContext : DbContext, IUnitOfWork
{
public TestContext()
: base("TestContext")
{
}
#region Implementation of IUnitOfWork
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion Implementation of IUnitOfWork
public DbSet<Destination> Destinations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DestinationMap());
}
Connection string defined in app config :
<connectionStrings>
<clear />
<add name="TestContext" providerName="System.Data.SqlClient" connectionString="Server=.\sqlexpress;Database=databaseName;Integrated Security=True;Connect Timeout=300;MultipleActiveResultSets=true;" />
</connectionStrings>
I've used StructureMap to inject DbContext in IUnitOfWork interface to implement context per request pattern.
This is StructureMap config :
ObjectFactory.Initialize(x => x.Scan(scanner =>
{
x.For<IUnitOfWork>().CacheBy(InstanceScope.Hybrid).Use<SpitfireContext>();
}));
When user creates new project I try to change connection string and force application create database for new project.
public static void ChangeDatabase(Guid guid)
{
var sqlConnectionStringBuilder =
new SqlConnectionStringBuilder(ConfigHelper.ActiveConnection);
sqlConnectionStringBuilder["Database"] = guid.ToString();
ConfigHelper.ActiveConnection = sqlConnectionStringBuilder.ToString();
Database.DefaultConnectionFactory =
new System.Data.Entity.Infrastructure.SqlConnectionFactory(ConfigHelper.ActiveConnectionString());
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<TestContext, MigrationConfiguration>());
using (var context = new TestContext())
{
context.Database.Initialize(true);
}
}
Application just creates database for first project and after that it tries migrate previews database.
I don't create DbConext object directly in my codes so I can't pass connection string to its constructor.
What do you suggest for my problem ?
What's wrong with my code ?
That's a tricky part - changing connection strings. It kind of works but has issues.
We had a lengthy discussion on that yesterday. Take a look at this (and related) post of mine - which describes some background of troubles for code based on similar approach.
I'd like to help you further if needed - but I'd need some more info...
Why do you say I don't create DbConext object directly in my codes so I can't pass connection string to its constructor ? You have the TestContext - why not pass it through there?
Also I'm not sure about setting the DefaultConnectionFactory, I'll recheck that. It's allowed to be set but it seems recommended on app startup only, not sure. Look up this link in the meantime (it's for EF6 but code is I think similar - and you can look up the source code) - http://msdn.microsoft.com/en-US/data/jj680699
We came to the conclusion that using the DbContext factory was beneficial in such cases.
Would adding a static property on your DbContext class to store the name of the database (Initial Catalog) and then changing the connecting string so that you insert the database name with string.Format work?
Then you could change the database name at any time the next time a context is made it'll be pointing to a new target.
Related
I'm writing an MVC C# application. I use dapper as a lightweight ORM. My connection strings are defined with server and initial catalog, and currently if I need to access a different database I define another connection string, and use Ninject bindings to use a particular connection string based on the manager i'm injecting it into, like so:
public class NinjectBindings : NinjectModule
{
public override void Load()
{
Bind<IDbConnection>().To<SqlConnection>()
.WhenInjectedInto<DashboardManager>()
.InRequestScope()
.Named("myDashboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString);
Bind<IDbConnection>().To<SqlConnection>()
.WhenInjectedInto<ScoreboardManager>()
.InRequestScope()
.Named("myScoreboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString);
}
}
Unfortunately this doesn't work if I have code in the same Manager that needs to call stored procedures that are on different databases than the initially specified catalog.
Question is: Can I just define one connection string, lose all the ninject binding stuff above, and simply change the Initial Catalog to a point to a different database on the fly?
Do you need both Named and WhenInjectedInto constraints for your bindings?
I believe you have a class that requires both connectionstrings, this could be achieved using Named binding:
Bind<IDbConnection>().To<SqlConnection>()
.InRequestScope()
.Named("myDashboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString);
Bind<IDbConnection>().To<SqlConnection>()
.InRequestScope()
.Named("myScoreboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString);
And your class can get both connections:
public class ClassWith2DbDependency // <-- I would question this class for SRP violation
{
private readonly IDbConnection _dashboardConnection;
private readonly IDbConnection _scoreboardConnection;
public ClassWith2DBDependency(
[Named("myDashboard")] IDbConnection dashboardConnection
[Named("myScoreboard")] IDbConnection scoreboardConnection)
{
_dashboardConnection = dashboardConnection;
_scoreboardConnection = scoreboardConnection;
}
public void WriteTo2Dbs()
{
// execute dashboard DB procedure
// execute scoreboard DB procedure
}
}
Can I just define one connection string, lose all the ninject binding
stuff above, and simply change the Initial Catalog to a point to a
different database on the fly?
Changing Initial Catalog doesn't affect an existing SqlConnection. It is possible to manage the dependencies yourself, but you still need 2 connectionstrings:
public class ClassWith2DbDependency
{
public void WriteTo2Dbs()
{
var dashboardCon = ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString;
using (SqlConnection connection = new SqlConnection(dashboardCon))
{
// execute dashboard DB procedure
}
var scoreboardCon = ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString;
using (SqlConnection connection = new SqlConnection(scoreboardCon))
{
// execute scoreboard DB procedure
}
}
}
However, I do NOT recommend this approach, the above class violates DI principle, by having Opaque Dependencies.
I haven't seen your code, but it doesn't sound like you are using Repository Pattern? This could be a good option...
I created a ASP.NET MVC website using .NET Core 2.2 using a SQLite database. So far it's working well. Trouble begins when I want to add SQLite-specific keywords to the connection string, such as
Data Source=~\\App_Data\\MyDb.db; Version=3; DateTimeFormat=UnixEpoch; DateTimeKind=Utc
Now I get
Keyword not supported: 'version'
I register the database like this
// ConfigureServices(IServiceCollection services)
var conn = Configuration.GetConnectionString("MyDB").Replace("~", _env.ContentRootPath);
services.AddDbContext<MyDBContext>(options => options.UseSqlite(conn));
Then MyDBContext has
public partial class MyDBContext : DbContext
{
public MyDBContext() { }
public SatrimonoContext(DbContextOptions<MyDBContext> options)
: base(options) { }
public virtual DbSet<Book> Book { get; set; }
}
Then I use it in my page Model
private SatrimonoContext _db;
public BookAccuracyListModel(SatrimonoContext dbContext)
{
_db = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
and from there I can access _db normally via LINQ.
Before posting here, I did plenty of research on the topic, and the best responses I found were this
This provider is Microsoft.Data.Sqlite. Those connection strings are
for System.Data.SQLite.
We support the following keywords: Cache, Data Source, Mode.
and this
The issue I was having was because I was trying to create a
SqlConnection instead of a SQLiteConnection. Making that change solved
my issue.
However, if I'm doing it "right", I'm not creating the SqlConnection and thus can't change it to SQLiteConnection. The other response doesn't include a solution.
So how do I get this to work the right way?
There is a thread in Github regarding the issue.
Microsoft.Data.Sqlite only supports three keywords:
Cache - Private or Shared
Data Source - The database file. Can be a URI filename.
Mode - ReadWriteCreate, ReadWrite, ReadOnly, or Memory.
No other keywords are supported for this namespace, however if you use the keywords you mentioned with System.Data.SQLite namespace, it will work, as they are keywords matched for System.Data.SQLite.
Your two options:
Remove the Version=3 keyword or any other unsupported keyword from the connection string
Change the namespace you use for your SQlite connection.
Expanding on Barr's response, the solution is to add System.Data.SQLite.Core to the project.
Then replace
var conn = Configuration.GetConnectionString("Satrimono").Replace("~", _env.ContentRootPath);
services.AddDbContext<SatrimonoContext>(options => options.UseSqlite(conn));
with
var connString = Configuration.GetConnectionString("Satrimono").Replace("~", _env.ContentRootPath);
var conn = new SQLiteConnection(connString);
services.AddDbContext<SatrimonoContext>(options => options.UseSqlite(conn));
That's it!
I'm developing a WPF application which depends on Entity Framework for data access.
At the first time installation I need to create a new connection string based on the User input, then updating App.Config according to that.
The problem is: after updating the App.Config file, Entity Framework doesn't detect the change and uses the old startup-time ConnectionString for instantiating the DbContext.
How can I update the Entity Framework's ConnectionString setting at runtime?
Entity Framework caches connection string, there isn't a method to force a refresh.
From this article: connection string given in DbContext constructor isn't cached then you can use this as workaround:
public class MyContext : DbContext {
public MyContext()
: base(ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString)
{
}
}
Had same issue today. Connection string is stored in cached App.Config file in obj folder.
Solution: Delete obj folder.
I had the same error. My new connection string was missing "Initial Catalog="
Global.cs
public class Global
{
public static string ConnectionString=String.Empty;
}
Store connection string as temporary in Global.cs first time after connection string saved in App.config.
DBContext.cs
public class DBContext: DbContext
{
public DbSet<User> UserInfo { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
String.IsNullOrEmpty(Global.ConnectionString) ? ConfigurationManager.ConnectionStrings["your_connectionstring"].ConnectionString
: Global.ConnectionString
);
}
}
Firstly, you have already defined connection string in your App.config.
Hope you enjoy!
I am building an MVC application that connect to diferent databases depending on the user that has log in.
For this i have 3 projects DAL using entity framework(DataBaseFirst) where i have extended the dbcontext so that i can pass the connectionstring like this:
public partial class ARACultivoEntities
{
public ARACultivoEntities(string nameOfConnectionString)
: base(nameOfConnectionString)
{
}
}
Note: I have the connections strings defined in the web.config of the mvc project.
There is also another project, Services where i have a genericService from where other service can inherit this like this:
public class GenericService<T> : IGenericService<T>
where T : class
{
protected ARACultivoEntities Db;
protected DbSet<T> Table;
public GenericService(string nameConnectionString)
{
if (string.IsNullOrEmpty(nameConnectionString))
{
throw new ArgumentNullException("nameConnectionString");
}
Db = new ARACultivoEntities(nameConnectionString);
Table = Db.Set<T>();
}
Now i save the name of the connection string in the user claims when he logs in and in the controllers i have something like this:
public class DeduccionController : Controller
{
private IGenericService<Deducciones> service;
public DeduccionController()
{
service = ServiceFactoryGeneric<Deducciones>.InitGenericService(GetClaimsUser.Cadena);
//GetClaimsUser.Cadena has the name of the connectionString
//ServiceFactoryGeneric<Deducciones>.InitGenericService do this:
// return new GenericService<T>(connectionString);
}
now i want to instead of having my own factories i want to use an Ioc Container and i have chosen unity for this, i am new to this, i've read some articles and i think i undsertand the basics but i dont know how to pass the connection string after the user has log in because my RegisterTypes hapen at the application start
public static void RegisterTypes(IUnityContainer container)
{
// this happen at application start
// string nameOfConnectionString = *user is still not loged in*
container.RegisterType<IGenericService<T>, GenericService<T>>(
new InjectionConstructor(nameOfConnectionString));
}
i been thinkin to try to tweet the code to register my types after the user has loged in but i dont think this a good idea..
i also have been thinking about adding a public method to my IGenericService so that i can set my connectionString after the service is constructed and implemented something like this:
public void SetConnectionString(string nameOfConnectionString)
{
Db.Database.Connection.ConnectionString = nameOfConnectionString;
//not sure if this actually works
}
then my controller will be something like this:
public class DeduccionController : Controller
{
private IGenericService<Deducciones> _service;
public DeduccionController(IGenericService<Deducciones> service)
{
_service = service;
_service.SetConnectionString(GetUserClaims.Cadena);
}
and let my RegisterTypes just with the:
container.RegisterType<IGenericService<T>, GenericService<T>>()
but since i new to this world of IoCs i am not sure if this is the best way
What would be the correct way to do this using Unity?
Thank you for reading.
I am sorry for my english not my first languague.
I recently had to do something similar by swapping connection strings based on a route parameter specifying a geo-location.
I would recommend building your own Unity LifetimeManager that acts in a instance per session scope. Register an object that acts as a configuration container for the connection string property.
[See Unity Lifetime Manager: http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.lifetimemanager(v=pandp.30).aspx]
Then you could inject that singleton instance of this configuration object into your controller and set the connection string property once a user has logged in. You could then inject that same singleton instance into a DbContext factory that instantiates your DbContext using the connection string specified in your configuration object.
Like I said, it may not be the most elegant solution, but I liked it better than having to pass a connection string through the many tiers of your application stack. Hope this helps.
I need to pass a WSID in the connectionstring of an Entity Framework context.
I dont want to update all the instanciations that i have, to pass a new connectionstring so i was wondering if there is a method to override that can be of help?
For information, i have this actually:
using(var context = new SampleEntities())
and i dont want to rewrite it like that:
using(var context = new SampleEntities(NewConnectionString))
I tried to override the CreateContext method without success...
With EntityFramework 6, it's not more possible to pass natively a connection string to the constructor of the context (there's no more constructor for that) so i had overload my context class to add the constructor
public partial class MyObjectEntities : DbContext
{
public MyObjectEntities(string connectionString)
: base(connectionString)
{
}
}
In addition, because it's not possible to update the connection string inside the context after instanciation, i had to make the desired connection string before instanciating of my context.
So to do that, i created a dll project (sharable between mutiples projects) able to configure my environnement and containing my desired WSID.
Here is the construtor of a connection string gave by Microsoft http://msdn.microsoft.com/en-US/en-en/library/bb738533%28v=vs.110%29.aspx