Assuming there is an ASP.NET MVC application that uses Entity Framework 6 with a code-first approach and StructureMap as IoC.
It also uses the Unit Of Work pattern.
Domain Class:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
IUnitOfWork and DbContext:
public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
public class Sample07Context : DbContext, IUnitOfWork
{
public DbSet<Product> Products { set; get; }
#region IUnitOfWork Members
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion
}
Business logic in service classes :
public interface IProductService
{
void AddNewProduct(Product product);
IList<Product> GetAllProducts();
}
public class ProductService: IProductService
{
IUnitOfWork _uow;
IDbSet<Product> _products;
public ProductService(IUnitOfWork uow)
{
_uow = uow;
_products = _uow.Set<Product>();
}
public void AddNewProduct(Product product)
{
_products.Add(product);
}
public IList<Product> GetAllProducts()
{
return _products.Include(x => x.Category).ToList();
}
}
Injecting the service class in controller
public class HomeController : Controller
{
private IProductService _productService;
private IUnitOfWork _uow;
public HomeController(IUnitOfWork uow, IProductService productService)
{
_productService = productService;
_uow = uow;
}
[HttpGet]
public ActionResult Index()
{
var list = _productService.GetAllProducts();
return View(list);
}
}
StructureMap Configuration that we call in app_start :
private static void initStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWork>().HttpContextScoped().Use(() => new Sample07Context());
x.ForRequestedType<IProductService>().TheDefaultIsConcreteType<EfProductService>();
});
//Set current Controller factory as StructureMapControllerFactory
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}
Everything works fine with the single database but in my scenario user can use multiple databases, I mean the user should be able to change the connection string at runtime. We create a separate database for each project that the user creates in the application.
Now the problem is that we inject DbContext into the service and DbContext reads the connection string from the web.config so when user changes the database we cannot set a new connection string to the DbContext.
What do you suggest?
In my experience, I used the Database First mode in EF 6. The DbContext would be generated like below when I add Entity Data Model.
public TestEntities()
: base("name=TestEntities")
{
}
The TestEntities represent the ConnectionString element in the App.Config
<connectionStrings>
<add name="TestEntities" connectionString="..." providerName="System.Data.EntityClient" />
</connectionStrings>
But you can change the default code to below.
public partial class TestEntities : DbContext
{
public TestEntities()
: base("name=TestEntities")
{
}
public TestEntities(string sConnectionString)
: base(sConnectionString)
{
}
...}
So you got two options to getting DB connection.
using the default. The EF will find the connection string in the config file.
passing the connection string to DbContext.
The code look like below.
EntityConnection entityConn =DBConnectionHelper.BuildConnection();
using (var db = new TestEntities(entityConn.ConnectionString))
{
....
}
As to the question How to build a EntityConnection?. Please see MSDN EntityConnection.
Hope it is helpful.
Thanks.
By default the name of the connection string to use in Entity Framework is inferred from the name of you DbContext class. However you can pass the connection string as a constructor parameter:
public class MyDbContext : DbContext, IUnitOfWork
{
public MyDbContext(string connectionString)
: base(connectionString)
{
}
}
Then you can configure StructureMap to pass in the current connection string e.g.
For<IUnitOfWork>().Use(ctx => new MyDbContext(TheConnectionStringToUse));
This could come from a static value that you set in your code, the current session etc.
I am going to suggest a completely different path. Assuming you have your connection strings set up in your web.config, which you say you do, why wouldn't you use web.debug.config and web.release.config transforrms to set your connection strings appropriately?
i.e. in web.debug.config
<connectionStrings>
<add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=IP,PORT\Instancename;
Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>
and a web.release.config as such
<connectionStrings xdt:Transform="Replace">
<add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=LIVEIP,PORT\Instancename;
Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>
A very thorough explanation is available here
public partial class YourDBContextClass
{
// Add a constructor to allow the connection string name to be changed
public YourDBContextClass(string connectionStringNameInConfig)
: base("name=" + connectionStringNameInConfig)
{
}
}
Add multiple connection strings to your web or app.config file.
in your program code:
YourDBContextClass dbcontext = new YourDBContextClass("alternateconnectionstringname");
The two approaches are good for two different situations:
The transform is good for deploying a connection string that only changes for the different evironments (test, production).
The approach of adding a constructor (which takes the connection string name) in a separate file to extend the partial dbcontext class allows the connection to be switched at runtime.
Add two different Connection String in App.Config File using different Name.
Set Current connection String Name in Entity Constructor using Overloading.
In Code File
public ASM_DBEntities()
: base("name=ASM_DBEntities")
{
}
public ASM_DBEntities(string conn)
: base("name=ASM_DBEntities1")
{
}
When we pass string with object then is use different connection string.
Related
I have an ASP.NET Core 6 MVC app that I created from a VS 2022 template. I'm writing a custom data access layer and business logic layer.
I know I can pass the _context from the controllers down through the BLL in the DAL, however I would prefer to have direct access from the DAL. I don't see any reason the BLL or the web code need to have anything to do with data access.
I've tried several examples of injection, but I can't seem to get any of them to work. Does anyone have a good solution?
Edit:
In Project.cs I have added DbContext:
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
I would like to get access to DbContext in my custom data access layer without having to pass DbContext all the way down from a Controller into my DAL contructor like so:
namespace MyProject.Code
public class CustomDataAccessLayer
{
public string[] GetCustomers()
{
DbConnection conn = DbContext.GetConnection();
//Whatever query logic I want to do here
Is there any way to access DbConext directly without having to pass it in from within a controller action?
Here you can read about how DI works exactly.
I'll provide some example of how you're supposed to do that.
first you need to have DataContext Class that extends DbContext Class that will be like this
using Microsoft.EntityFrameworkCore;
namespace MyProject.Context
{
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<MyEntity> Entities { get; set; }
}
}
In the Program.cs you need to add theDataContextlike this
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(builder.Configuration
.GetConnectionString("DefaultConnection")); // for example if you're holding the connection string in the appsettings.json
});
and then in the Logic class that you want to use it in make a data member in the class of type DataContext
private readonly DataContext _dataContext;
and your constructor must take DataContext as parameter and Initialize your constructor will look like this
public ClassName(DataContext dataContext)
{
_dataContext = dataContext;
}
you don't need to add dbcontext to a controller, nobody does it if there is a data access layer, DI will automatically inject the context
public class CustomDataAccessLayer
{
private readonly ApplicationDbContext _context;
public CustomDataAccessLayer(ApplicationDbContext context)
{
_context = context;
}
public string[] GetCustomers()
{
//your code
}
}
with json :
var constr = builder.Configuration["ConnectionStrings:Default"];
builder.Services.AddDbContext<AppDbContext>(opt => {
opt.UseSqlServer(constr);
});
Json :
"ConnectionStrings": {
"Default": "Server=Victus;Database=TeamDb;Trusted_Connection=true;"
}
AppDbContext.cs :
public class AppDbContext:DbContext {
public AppDbContext(DbContextOptions <AppDbContext> options) : base(options) { }
public DbSet<team> teams { get; set; }
}
Home Controller :
private AppDbContext _appDbContext;
public HomeController(AppDbContext appDbContext) {
_appDbContext = appDbContext;
}
public IActionResult Index() {
return View(_appDbContext.teams);
}
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.
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.
I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.
I'm trying to set a connection string dynamically to the MyDataContext inheriting DbContext and I got this error:
No connection string named 'Data Source=myserver\mine;Initial Catalog=MyDb;User ID=user1;Password=mypassword;Connect Timeout=300' could be found in the application config file.
Here's what my code look like:
public class DataContext : DbContext, IDataContext, IDataContextAsync
{
private readonly Guid _instanceId;
public DataContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
_instanceId = Guid.NewGuid();
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
//and so on...
}
public class MyDataContext : DataContext
{
public MyDataContext (string nameOrConnectionString)
:base(nameOrConnectionString)
{
}
}
I'm wondering why this is happening. As what I've understand, the constructor can accept either connection string name and connection string. In this case Entity Framework seem to expect a name.
Please help.
Thanks :)
You probably want DbContext(String) and not DataContext(String) given the EF tag (and you referencing nameOrConnectionString argument).
public class MyDataContext : DbContext
{
public MyDataContext(String nameOrConnectionString)
: base(nameOrConnectionString)
{
}
}
Otherwise the DataContext is looking for a file source, which could be the problem you're seeing.
Try copying the connections string to the .config file in the MVC project (i.e main project) and make sure that MVC project as a startup.