Run sql query at the beginning of an Oracle session using DbContext - c#

The requirement is to execute a sql query using DbContext (EF 5 in DatabaseFirst mode) at the beginning of every Oracle session that DbContext might invoke.
Putting this in the constructor gives inconsistent results because there are instances where this sql query is not run at all when expected.
The setup is EF5 in DBFirst mode connecting to Oracle10gR2 using ODP.NET v12 Managed driver.
public partial class MyContext : DbContext
{
public MyContext(string connectionString)
: base(connectionString)
{
Database.ExecuteSqlCommand(Constants.SqlQuery);
}
}
I instantiate the context by passing the connecting string because the connection string needs to be dynamic as follows:
using(var context = new MyContext(GetConnectionString()))
{
...
...
context.SaveChanges();
}
Is there a way to ensure that this query is always run whenever an Oracle session is created?

You can extend the DbContext class and implement each method to do so. For example....
public KashDbContext : DbContext
{
public int SaveChanges()
{
Database.ExecuteSqlCommand(Constants.SqlQuery);
base.SaveChanges();
}
//Do for all methods
}

Just implement the OnContextCreated() method in your partial class:
partial void OnContextCreated()
{
this.ExecuteStoreCommand(Constants.SqlQuery, new object[] { });
}

Related

Change DB name for Entity Framework DbContext

I have inherited an application which is using Entity Framework to access a SQL Server database. The DbContext class has the constructor as shown below, where BuildingPermits is the name of the initial catalog.
I would like to be able to switch between databases (with the same connection) via a config file instead of changing the code.
How can I accomplish this?
public BuildingPermitsDbContext() : base("BuildingPermits")
{
Database.SetInitializer<BuildingPermitsDbContext>(null);
}
You can try this.
using System.Configuration;
public BuildingPermitsDbContext() : base(DatabaseName())
{
Database.SetInitializer<BuildingPermitsDbContext>(null);
}
private static string DatabaseName()
{
var db = ConfigurationManager.AppSettingss["desiredDbName"];
return db;
}

Database.Migrate() raises SQL Server Express Login Failed Error

I'm using EF Core version 2.1.11 with SQL Server Express version 12.0.2000.
I have my entity:
public class MyEntity
{
public string Id { get; set; }
public int Age { get; set; }
}
and my Db context:
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyEntity>();
}
}
In order to create migrations using dotnet ef migrations add InitialCreate, I also have a design-time factory defined like:
public class MyContextFactory : IDesignTimeDbContextFactory<MyContext>
{
public MyContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder<MyContext>()
.UseSqlServer("server=localhost\\SQLEXPRESS;Initial Catalog=MyTestDB;Integrated Security=true")
.Options;
return new MyContext(options);
}
}
Note that, in my SQL Express, I have not created a database named MyTestDB. I want EF Core to create this database and apply the migrations. So, my main method that attempts this is:
class Program
{
static void Main(string[] args)
{
using (var ctx = new MyContextFactory().CreateDbContext(Array.Empty<string>()))
{
ctx.Database.Migrate();
}
}
}
Now, the line ctx.Database.Migrate() raises the exception:
System.Data.SqlClient.SqlException: 'Cannot open database "MyTestDB" requested by the login. The login failed.
Login failed for user
This exception is being raised because the MyTestDB is not in SQL Express server at the time of opening the connection. What is strange is, when I run the main method, it raises this exception like 15 times (which is suppressed unless the exception settings are enabled) but when the program exits, and I check the database, the database is actually created and the migrations are applied.
My question is how can I avoid this exception being raised (I do not want to create the database before running the code)? What is the proper way to use the connection string and the Database.Migrate() method?

C# .NET Entity Framework multi-tenant best practice

Consider the following code segment:
public class DatabaseContext : DbContext
{
public DatabaseContext(String connectionString) : base(connectionString)
{
}
}
public class ContextNameDatabaseContext : DatabaseContext
{
public ContextNameDatabaseContext(String connectionString) : base(connectionString)
{
}
}
Would one say it is best practice when building the back-end for a multi-tenant solution where each client has its own database and maintain the data state until a user logs out / off?
Developer using these classes in this instance will need to be aware and careful as to when and how the classes are being used where the 'DatabaseContext' class acts as a base to the 'ContextNameDatabaseContext' class.
Please advise on any thoughts or suggestions.
One approach is to keep all the database connection strings as parameters in the database. However you have to assure that its encrypted.
Then at your DB layer you can pass the connection as parameter in plain text after decrypting and constructing your connection string accordingly:
public class MyDatabase: DbContext
{
public MyDatabase(string connString)
{
this.Database.Connection.ConnectionString = connString;
}
public DbSet<Order> Orders{ get; set; }
}
You can also use IOptions if you are using .NET Core to inject the connection string as a dependency.

DbContext with Ninject ADO.NET

I am working on a big project that 80% completed (Some features need to be implemented though).But recently we discovered that the project doesn't allow concurrent requests (I mean multiple users request to same repository). Sometime we get null referece & sometimes "Executed can not open available connection , connection state is closed" etc.
Our source code is strongly restricted outside of the world. Here is some code.Let me know if there is any architectural problem, as architectural guys left company. It's using ninject 3.0. I already used InRequestScope() for all manager's repositories but no luck
Update: I am not using any ORM here, I am trying to connect SqlServer through data adapter in my DbContext class
public class DbContext
{
//execute query , nonquery etc using adapter & datatable
//Example
var dt=new DataTable();
_adapter=new _dbfactory.CreateAdapter();
_adapter.Fill(dt);
return dt;
}
//MyController
public class MyController
{
private readonly IMyManager_iMyManager;
public MyController(IMyManager iMyManager){_iMyManager=iMyManager}
public ActionResult Save()
{
_iMyManager.Save()
}
}
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
Public DataTable GetDataTable()
{
try
{
_dbContext.Open();
_iMyRepository.GetDataTable()
}
catch(Exception ex){}
finally{_dbContext.Close()}
}
}
// here is the repository
Public class MyRepository:IMyRepository
{
public _dbContext;
public MyRepository(DbContext dbContext)
{
_dbContext=dbContext;
}
public DataTable GetDataTable()
{ return _dbContext.ExecuteQuery()}
}
Finally Here is our ninject binding
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
there can have some typo in my code as I wrote everything in so editor
I think you did this too complicated in Ninject Dependency Resolver.
You shouldn't create DbContext with a new keyword. Instead you should make Ninject to be resolving DbContext in request scope or in thread scope.
To register DbContext you can do it like this:
kernel.Bind<DbContext>().To<MyDbContext>().WithConstructorArgument("someArgument", "someValue").InRequestScope();
kernel.Bind<IMyManager>().To<MyManager>().InRequestScope();
kernel.Bind<IMyRepository>().To<MyRepository>().InRequestScope();
You don't need to precise the constructor argument to DbContext as DbContext is only once registered in the Ninject.
You can also register DbContext to a DbContextProvider class and there you can add some specific logic to resolve object.
Example:
kernel.Bind<DbContext>().ToProvider<MyDbContextProvider>().InRequestScope();
internal class MyDbContextProvider : Ninject.Activation.IProvider
{
public object Create(IContext context)
{
return new MyDbContext("connectionStringArgument";
}
public Type Type { get { return typeof (MyDbContext); } }
}
I hope this helps.
You need to remove this initialization in the MyManager since you pass the initialized DbContext via IoC.
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
You also need to remove the finally block in the GetDataTable in the MyManager class since as a rule of thumb, if the object is initialized via IoC, it should be destroyed by IoC as well.
finally{_dbContext.Close()}
If you are initializing something in the field level then why would you initialize it again from the constructor?
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new DbContext("someParameter","connectionstring");
public MyManager(IMyRepository iMyRepository, DbContext dbContext)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
This may also be a typo. Either remove the _dbContext initialization from the constructor or delegate the task of initialization to the caller of this class.
Multiple initialization can also be the problem. since you are doing dbcontext initialization both in NinjectDependencyResolver() and MyManager. For this you are getting two different exceptions. This is a platform design issue i guess
Two problems:
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
The new that is created for the field will be overwritten when the constructor is called.
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
You create the context here once and pass it to each object creation. So you are still reusing the context object instead of creating it for each request scope.

Set EF ConnectionString at runtime from subdomain for multi-tenancy setup

Currently, my DbContext class has this code:
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
What I'd like to do, is inspect the subdomain and use that for the database name, so something like http://abc.mysite.com/ would use a connection string with database name abc.
But how do I manipulate the value of "DefaultConnection" in the constructor?
DBContext will take a name or a connection string in its constructor. That constructor is not usually exposed if you have a generated model.
You can use a partial class to expose that constructor:
public partial class DataEntities
{
public DataEntities(string connectionString) : base(connectionString)
{
}
}
I have done that before. My project was set up for DI with Castle Windsor and one of my IWindsorInstallers was DataAccessInstaller responsible for registering, among other classes like repositories, my database context and here is the relevant code:
container.Register(Component
.For<MyDatabaseContext>().Forward<DbContext>()
.ImplementedBy<MyDatabaseContext>()
.LifestylePerWebRequest()
.UsingFactoryMethod(context =>
{
return MyDatabaseContextFactory.Create(HttpContext.Current.Request.Url);
}));
You can have several connection strings set up in your web.config matching your domain.
My context factory implementation:
public static class MyDatabaseContextFactory
{
public static MyDatabaseContext Create(Uri uri)
{
return new MyDatabaseContext(uri.GetTopDomain());
}
}
If you just have a simple project and don't even have DI, you can still make use of a factory that finds out what the website use and instantiates a database context with the appropriate connection string.
Needless to say, your current database context constructor doesn't have to change.

Categories