I am trying to figure out how to create a second DB context in EF7 RC1. In the past I could use a constructor with :base("connectionName") but that no longer seems an option since it says cannot convert string to System.IServiceProvider.
My second context code is as follows:
public class DecAppContext : DbContext
{
public DecAppContext()
// :base("DefaultConnection")
{
}
public DbSet<VignetteModels> VignetteModels { get; set; }
public DbSet<VignetteResult> Result { get; set; }
}
}
In my config.json I have the connection specified:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-xxxxx...;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
In my configure services section of my startup I have both contexts added:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]))
.AddDbContext<DecAppContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
The applicationDB context works fine since I can create a user and login without issue
However when I try to access the other context as in my controller via:
private DecAppContext db = new DecAppContext();
var vignette = db.VignetteModels.SingleOrDefault(v => v.CaseId == vid);
I get the error:
No database providers are configured. Configure a database provider by
overriding OnConfiguring in your DbContext class or in the
AddDbContext method when setting up services.
Any working examples in EF7 RC1 with multiple db contexts and accessing them would be much appreciated.
First of all I would recommend you the article from the wiki of EntityFramework on GitHub. The article describes many ways to define DbContext, which references to a section of appsettings.json. I personally prefer the way with the usage of [FromServices] attribute.
The code could be about the following:
First of all you defined appsettings.json with the following content
{
"Data": {
"ApplicationDbConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"DecAppDbConnectionString": "Server=Server=(localdb)\\mssqllocaldb;Database=DecAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
where you define two connection strings.
Seconds you declare the classes DecAppContext and ApplicationDbContext which have DbContext as the base class. The simplest form will be just
public class ApplicationDbContext : DbContext
{
}
public class DecAppContext : DbContext
{
}
without any DbSet properties.
Third Step. You use Microsoft.Extensions.DependencyInjection to inject the database contexts. To do this you need just include in Startup.cs something like
public class Startup
{
// property for holding configuration
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
// save the configuration in Configuration property
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options => {
options.UseSqlServer(Configuration["Data:ApplicationDbConnectionString"]);
})
.AddDbContext<DecAppContext>(options => {
options.UseSqlServer(Configuration["Data:DecAppDbConnectionString"]);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
}
}
Se create two DbContext (DecAppContext and ApplicationDbContext) using the configuration "Data:DecAppDbConnectionString" and "Data:ApplicationDbConnectionString".
Now we can just use the context in the controller. For example
[Route("api/[controller]")]
public class UsersController : Controller
{
[FromServices]
public ApplicationDbContext ApplicationDbContext { get; set; }
[FromServices]
public DecAppContext DecAppContext { get; set; }
[HttpGet]
public IEnumerable<object> Get() {
var returnObject = new List<dynamic>();
using (var cmd = ApplicationDbContext.Database.GetDbConnection().CreateCommand()) {
cmd.CommandText = "SELECT Id, FirstName FROM dbo.Users";
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
var retObject = new List<dynamic>();
using (var dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
var dataRow = new ExpandoObject() as IDictionary<string, object>;
for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++)
dataRow.Add(
dataReader.GetName(iFiled),
dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {}
);
retObject.Add((ExpandoObject)dataRow);
}
}
return retObject;
}
}
}
or the same using async/await:
[Route("api/[controller]")]
public class UsersController : Controller
{
[FromServices]
public ApplicationDbContext ApplicationDbContext { get; set; }
[FromServices]
public DecAppContext DecAppContext { get; set; }
[HttpGet]
public async IEnumerable<object> Get() {
var returnObject = new List<dynamic>();
using (var cmd = ApplicationDbContext.Database.GetDbConnection().CreateCommand()) {
cmd.CommandText = "SELECT Id, FirstName FROM dbo.Users";
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
var retObject = new List<dynamic>();
using (var dataReader = await cmd.ExecuteReaderAsync())
{
while (await dataReader.ReadAsync())
{
var dataRow = new ExpandoObject() as IDictionary<string, object>;
for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++)
dataRow.Add(dataReader.GetName(iFiled), dataReader[iFiled]);
retObject.Add((ExpandoObject)dataRow);
}
}
return retObject;
}
}
}
One can just declare the property public ApplicationDbContext ApplicationDbContext { get; set; } with the attribute [FromServices] and ASP.NET initialize it from the context injected in ConfigureServices. In the same way one can use the second context DecAppContext whenever you need it.
The above code example will execute SELECT Id, FirstName From dbo.Users in the database context and return JSON data in the form [{"id":123, "firstName":"Oleg"},{"id":456, "firstName":"Xaxum"}]. The conversion of property names from Id and FirstName to id and firstName will be done automatically during serialization because of usage AddJsonOptions in ConfigureServices.
UPDATE: I have to reference the announcement. The next version of MVC (RC2) will require to change the above code to use [FromServices] as additional parameter (of method Get() for example) instead of usage public property [FromServices] public ApplicationDbContext ApplicationDbContext { get; set; }. One will need to remove the property ApplicationDbContext and to add additional parameter to Get() method: public async IEnumerable<object> Get([FromServices] ApplicationDbContext applicationDbContext) {...}. Such changes can be easy done. See here and example of the changes in the demo example of MVC:
[Route("api/[controller]")]
public class UsersController : Controller
{
[HttpGet]
public async IEnumerable<object> Get(
[FromServices] ApplicationDbContext applicationDbContext,
[FromServices] DecAppContext decAppContext)
{
var returnObject = new List<dynamic>();
// ... the same code as before, but using applicationDbContext
// and decAppContext parameters instead of ApplicationDbContext
// and DecAppContext properties
}
Related
At my current project(blazor server side) I want to start using the session storage for user data like roles and names.
I've tried Blazored.SessionStorage and AspNetCore.Components.Server.ProtectedBrowserStorage.
The problem I'm facing is, that I just can't get the value(it's always null) and I don't know why.
Code I'm using:
public void GetUserInfo()
{
var x = sessionStorage.GetAsync<string>("Name");
var y = sessionStorage.GetAsync<string>("Email");
string Name = x.ToString();
string Email = y.ToString();
}
And
[Inject] public ProtectedSessionStorage sessionStorage { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
string Name = Helper.Name;
string Email = Helper.Email;
await sessionStorage.SetAsync("Name", Name);
await sessionStorage.SetAsync("Email", Email);
var x = sessionStorage.GetAsync<string>("Name");
var y = sessionStorage.GetAsync<string>("Email");
Name = x.Result.Value;
Email = y.Result.Value;
}
Thanks to everyone in advance and have a great day! :)
DO NOT USE THIS SOLUTION AS IS. WHEN I GET THE TIME I WILL UPDATE IT TO A WORKING SOLUTION
I suggest adding this as an injected object using Dependency Injection.
Create a class to hold this information and add is as a Scoped service.
Class:
public class UserInfo : IUserInfo //Create an interface
{
public static Name { get; set; }
public static Email { get; set; }
}
Injection (Program.cs on .NET 6):
public static async Task Main(string[] args)
{
//For WSAM
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//For Server
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddScoped<IUserInfo, UserInfo>(); //Scoped Service injection
}
Add data to injected service:
[Inject]
public IUserInfo UserInfo { get; set; }
protected override void OnInitialized() //Use whatever Life Cycle methods works for your implementation
{
UserInfo.Name = Helper.Name;
UserInfo.Email = Helper.Email;
}
Usage example:
#inject IUserInfo UserInfo
#page "/"
<div>#UserInfo.Name</div>
<div>#UserInfo.Email</div>
I am working on an ASP.NET Core Web API application. My API will accept a country name as one of the input parameter from request body.
Due to nature of the application, we have country wise database with same schema. I have created DbContext for one of the databases and want to initialize the DbContext by the passing the connection string based on input request parameter value.
I have created factory method to return the needed database context based on the parameter passed to the factory method. However, the challenge I am facing is, while initializing the factory class as DI from controller, object of factory class is instantiated before the controller action is called. Hence, parameter value in factory method is empty.
How can I pass a parameter in runtime to initialize an object using dependency injection?
Here is the code...
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDBContext : DbContext
{
public MyDBContext(DbContextOptions<MyDBContext> options)
: base(options)
{
}
public virtual DbSet<Student> Students { get; set; }
}
public interface IDbContextFactory
{
public MyDBContext GetDbContext(string
connectionString);
}
public class DbContextFactory : IDbContextFactory
{
public MyDBContext GetDbContext(string connectionString)
{
MyDBContext context = null;
if (!string.IsNullOrWhiteSpace(connectionString))
{
DbContextOptionsBuilder<MyDBContext> _dbContextOptionsBuilder = new DbContextOptionsBuilder<MyDBContext>().UseSqlServer(connectionString);
context = new MyDBContext(_dbContextOptionsBuilder.Options);
}
return context;
}
}
public interface IRepository
{
Student GetData();
}
public class Repository : IRepository
{
private MyDBContext _context;
public Repository(IDbContextFactory dbContextFactory)
{
// Here I need connection string based on input parameter (country) from request to dynamically generate country specific connection string
string connectionString = string.Empty;
_context = dbContextFactory.GetDbContext(connectionString);
}
public Student GetData()
{
return _context.Students.FirstOrDefault();
}
}
public interface IServiceAgent
{
Student GetData();
}
public class ServiceAgent : IServiceAgent
{
IRepository _repository;
public ServiceAgent(IRepository repository)
{
_repository = repository;
}
public Student GetData()
{
return _repository.GetData();
}
}
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly IServiceAgent _serviceAgent;
public HomeController(IServiceAgent serviceAgent)
{
_serviceAgent = serviceAgent;
}
[HttpGet]
public Student Get(string country)
{
return _serviceAgent.GetData();
}
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IServiceAgent, ServiceAgent>();
services.AddScoped<IRepository, Repository>();
services.AddScoped<IDbContextFactory, DbContextFactory>();
services.AddScoped<DetermineCountryFilter>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I am trying to extend the AuditLog entity in ASPNETBOILETPLATE framework in order to add some new properties to it. I have tried to extend the AuditLog class (ExtendedAuditInfo) and implement a customised version of AuditStore Class (ExtendedAuditStore). However, I am not able to inject my new ExtendedAuditInfo in the constructor and receive two error messages regarding unmatching input parameters in the Constructor and SaveAsync method.
Class ExtendedAuditInfo:
public class ExtendedAuditInfo : AuditInfo
{
// Some properties
}
Class ExtendedAuditStore:
public class ExtendedAuditStore : AuditingStore
{
public ExtendedAuditStore(IRepository<ExtendedAuditInfo, long> auditLogRepository)
: base(auditLogRepository)
{
}
public override Task SaveAsync(ExtendedAuditInfo auditInfo)
{
if (!string.IsNullOrEmpty(auditInfo.Parameters) && auditInfo.Parameters != "{}")
{
var parameters = JsonConvert.DeserializeObject<AuditParameterInput>(auditInfo.Parameters);
if (parameters != null)
auditInfo.CustomData = parameters.Input.Id.ToString();
}
return base.SaveAsync(auditInfo);
}
}
The errors are:
cannot convert from 'Abp.Domain.Repositories.IRepository<SixB.Serafina.Auditing.ExtendedAuditInfo, long>' to 'Abp.Domain.Repositories.IRepository<Abp.Auditing.AuditLog, long>'
and
no suitable method found to override
The procedure above is based on the idea that I found Here
I found the solution based on the official document of How To Extend Existing Entities.
In order to extend the AuditLog class, inheritance must be used. Therefore a new class, let's say ExtendedAuditInfo needs to be inherited from AuditLog.
public class ExtendedAuditLog : AuditLog
{
public ExtendedAuditLog()
{
}
public ExtendedAuditLog(AuditInfo auditInfo)
{
this.BrowserInfo = auditInfo.BrowserInfo;
this.ClientIpAddress = auditInfo.ClientIpAddress;
this.ClientName = auditInfo.ClientName;
this.CustomData = auditInfo.CustomData;
this.Exception = auditInfo.Exception?.Message.ToString() + "";
this.ExecutionDuration = auditInfo.ExecutionDuration;
this.ExecutionTime = auditInfo.ExecutionTime;
this.ImpersonatorTenantId = auditInfo.ImpersonatorTenantId;
this.ImpersonatorUserId = auditInfo.ImpersonatorUserId;
this.MethodName = auditInfo.MethodName;
this.Parameters = auditInfo.Parameters;
this.ReturnValue = auditInfo.ReturnValue;
this.ServiceName = auditInfo.ServiceName;
this.TenantId = auditInfo.TenantId;
this.UserId = auditInfo.UserId;
}
//new properties
}
This class has to be added to the context and obviously, a new migration needs to be run in order to add the new properties.
public class ProjectDbContext : AbpZeroDbContext<Tenant, Role, User, ProjectDbContext >
{
/* Define a DbSet for each entity of the application */
public SerafinaDbContext(DbContextOptions<SerafinaDbContext> options)
: base(options)
{
}
public virtual DbSet<County> Counties { get; set; }
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Currency> Currencies { get; set; }
public virtual DbSet<OrganisationType> OrganisationTypes { get; set; }
public virtual DbSet<ExtendedAuditLog> ExtendedAuditLogs { get; set; }
}
Finally, in the ExtendedAuditStore class, IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository has to be injected as a second parameter of the constructor and can be used to insert the extended entity.
public class ExtendedAuditStore : AuditingStore
{
IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository;
public ExtendedAuditStore(
IRepository<AuditLog, long> auditLogRepository,
IRepository<ExtendedAuditLog, long> extendedAuditLogRepository
)
: base(auditLogRepository)
{
_extendedAuditLogRepository = extendedAuditLogRepository;
}
public override async Task SaveAsync(AuditInfo auditInfo)
{
if (auditInfo.Exception != null)
await base.SaveAsync(auditInfo);
var auditLog = new ExtendedAuditLog(auditInfo);
//new properties can be set here
await _extendedAuditLogRepository.InsertAsync(auditLog);
}
}
Also, instead of inheriting from AuditingStore, a new implementation for IAuditingStore can be created and injected into application services.
UPDATE:
Finally, all you need is to replace the default AuditingStore in StartUp class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAuditingStore, ExtendedAuditStore>();
}
I used following only controller in my project out of entire following project.
https://github.com/graphql-dotnet/examples/blob/master/src/AspNetWebApi/Example/Controllers/GraphQLController.cs
Here Controller file is as below.
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using GraphQL.Http;
using GraphQL.Instrumentation;
using GraphQL.Types;
using GraphQL.Validation.Complexity;
namespace GraphQL.GraphiQL.Controllers
{
public class GraphQLController : ApiController
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _executer;
private readonly IDocumentWriter _writer;
private readonly IDictionary<string, string> _namedQueries;
public GraphQLController(
IDocumentExecuter executer,
IDocumentWriter writer,
ISchema schema)
{
_executer = executer;
_writer = writer;
_schema = schema;
_namedQueries = new Dictionary<string, string>
{
["a-query"] = #"query foo { hero { name } }"
};
}
// This will display an example error
[HttpGet]
public Task<HttpResponseMessage> GetAsync(HttpRequestMessage request)
{
return PostAsync(request, new GraphQLQuery { Query = "query foo { hero }", Variables = null });
}
[HttpPost]
public async Task<HttpResponseMessage> PostAsync(HttpRequestMessage request, GraphQLQuery query)
{
var inputs = query.Variables.ToInputs();
var queryToExecute = query.Query;
if (!string.IsNullOrWhiteSpace(query.NamedQuery))
{
queryToExecute = _namedQueries[query.NamedQuery];
}
var result = await _executer.ExecuteAsync(_ =>
{
_.Schema = _schema;
_.Query = queryToExecute;
_.OperationName = query.OperationName;
_.Inputs = inputs;
_.ComplexityConfiguration = new ComplexityConfiguration { MaxDepth = 15 };
_.FieldMiddleware.Use<InstrumentFieldsMiddleware>();
}).ConfigureAwait(false);
var httpResult = result.Errors?.Count > 0
? HttpStatusCode.BadRequest
: HttpStatusCode.OK;
var json = _writer.Write(result);
var response = request.CreateResponse(httpResult);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
}
public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public Newtonsoft.Json.Linq.JObject Variables { get; set; }
}
}
When I try to call this controller, it gives me following error.
InvalidOperationException: Unable to resolve service for type 'GraphQL.Http.IDocumentWriter' while attempting to activate 'MyWebAppNamespace.Controllers.GraphQLController'.
Do I need any startup configuration or I can use this standalone controller? What am I missing?
Do I need any startup configuration or I can use this standalone controller? What am I missing?
As the error describes, it happens because the DI container cannot resolve service for IDocumentWriter.
It seems that you're missing some service registration. Did you forget to add the IDocumentWrite service ?
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
services.AddSingleton<IDocumentWriter, DocumentWriter>();
// ...
services.AddSingleton<ISchema, StarWarsSchema>();
// ...
}
For more details, see startup here
or I can use this standalone controller?
No. You're not supposed to do that. That's because your controller depends on the IDocumentExecuter, IDocumentWriter, and ISchema service. They're all resolved by Dependency Injection. You have to register all the services before resolve the controller.
Please can someone help me because I am getting confused.
I have an Entity like this:
public class Code
{
public int ID { get; set; }
public int UserID { get; set; }
public string CodeText { get; set; }
}
and an Interface like this:
public interface ICodeRepository
{
IQueryable<Code> Codes { get; }
void AddCode(Code code);
void RemoveCode(Code code);
Code GetCodeById(int id);
}
and a Repository like this:
public class SQLCodeRepository : ICodeRepository
{
private EFSQLContext context;
public SQLCodeRepository()
{
context = new EFSQLContext();
}
public IQueryable<Code> Codes
{
get { return context.Codes; }
}
public void AddCode(Code code)
{
context.Codes.Add(code);
context.SaveChanges();
}
public void RemoveCode(Code code)
{
context.Codes.Remove(code);
context.SaveChanges();
}
public Code GetCodeById(int id)
{
return context.Codes.Where(x => x.ID == id).FirstOrDefault();
}
}
and a Context like this:
public class EFSQLContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Code> Codes { get; set; }
public DbSet<PortfolioUser> PortfolioUsers { get; set; }
}
If I declare my controller like this:
public class SearchController : Controller
{
private ICodeRepository cRepo;
public SearchController(ICodeRepository codeRepository)
{
cRepo = codeRepository;
}
}
and then try to do cRepo.GetCodeById(1) nothing happens. But if I declare private ICodeRepository rep = new SQLCodeRepository and then call rep.GetCodeById(1) I can see the method in the Repository being called.
What am I doing wrong?
It looks like from the constructor signature, you are going to be doing some dependency injection. The step you are missing is to set up a DI container using a tool like Castle Windsor. You then configure the MVC resolver to use the DI container to give you the correct implementation of ICodeRepository.
See this
You'll need to create a resolver that implements IDependencyResolver and IDependencyScope and a controller factory that inheritsDefaultControllerFactory
Once you have those you can do something like the following:
MyContainer container; // this needs to be a class level member of the asax
var configuration = GlobalConfiguration.Configuration;
container = new MyContainer() // may need additional stuff here depending on DI tool used
configuration.DependencyResolver = new MyDependancyResolver(container);
var mvcControllerFactory = new MyFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory);
You would call the above code from the asax Application_Start()
See this answer for more specifics on using Ninject and MVC3