Autofac resolving parameters in runtime - c#-4.0

Let's get straight.
I have interface and class like this:
public interface IDataBase
{
DataTable GetSomeTableData();
}
My class:
public class DataBase : IDataBase
{
private readonly string _connectionString;
public DataBase(string connectionString)
{
this._connectionString = connectionString;
}
public DataTable GetSomeTableData()
{
using (SqlConnection cn = new SqlConnection(_connectionString))
{
cn.Open();
// some select
}
}
}
I'm using Autofac to inject that class:
var builder = new ContainerBuilder();
builder.RegisterType<DataBase>().As<IDataBase>).WithParameter("connectionString", "my connection string");
var container = builder.Build();
var database = container.Resolve<IDataBase>();
var tableData1 = database.GetSomeTableData();
// change connection string ?????????????????
var tableData2 = database.GetSomeTableData();
I need to get table data from one DB and another DB. How can I change connection string after have registered class? You may give another exapmle..

There are many ways to do it. One would be to create and inject a service instead of just plain connection string.
public interface IConnectionStringProvider
{
public string ConnectionString { get; set }
}
public class ConnectionStringProvider
{
public string ConnectionString { get; set }
}
var builder = new ContainerBuilder();
builder.RegisterType<DataBase>()
.As<IDataBase>);
builder.RegisterType<ConnectionStringProvider>)
.As<IConnectionStringProvider>
.SingleInstance();
var container = builder.Build();
var database = container.Resolve<IDataBase>();
var connStringProvider = container.Resolve<IConnectionStringProvider>();
var tableData1 = database.GetSomeTableData();
connStringProvider.ConnectionString = "...";
var tableData2 = database.GetSomeTableData();
The DataBase would then use that service:
public class DataBase : IDataBase
{
private readonly IConnectionStringProvider _connectionStringProvider;
public DataBase(IConnectionStringProvider connectionStringProvider)
{
this._connectionStringProvider = connectionStringProvider;
}
public DataTable GetSomeTableData()
{
using (SqlConnection cn = new SqlConnection(_connectionStringProvider.ConnectionString))
{
cn.Open();
// some select
}
}
}

Related

Login failed for user ''. using UserAssignedManagedIdentity while fetching data from AzureSQL

I have created a Managed Identity (User Assigned) using Azure portal.
I attached that MSI with Azure App Service
Added appropriate permissions for the MSI at Azure SQL (Database)
In this implementation I am using Microsoft.EntityFrameworkCore version 2.2.6
I have the following code :
IDBAuthTokenService.cs
public interface IDBAuthTokenService
{
Task<string> GetTokenAsync();
}
AzureSqlAuthTokenService.cs
public class AzureSqlAuthTokenService : IDBAuthTokenService
{
public readonly IConfiguration _configuration;
public AzureSqlAuthTokenService(IConfiguration configuration)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public async Task<string> GetTokenAsync()
{
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions{ManagedIdentityClientId = _configuration[C.AppKeys.UserAssignedClientId]});
var tokenRequestContext = new TokenRequestContext(new[]{_configuration[C.AppKeys.AzureSQLResourceId]});
var token = await credential.GetTokenAsync(tokenRequestContext, default);
return token.Token;
}
}
TestDbContext.cs:
public partial class TestDbContext : DbContext
{
public TestDbContext()
{
}
public TestDbContext(IDBAuthTokenService tokenService, DbContextOptions<TestDbContext> options) : base(options)
{
var connection = this.Database.GetDbConnection() as SqlConnection;
connection.AccessToken = tokenService.GetTokenAsync().Result;
}
public virtual DbSet<HealthCheckData> HealthCheckData { get; set; }
}
TestReportServiceProvider.cs
public class TestReportServiceProvider : IReportService
{
private readonly TestDbContext _objDBContext;
public TestReportServiceProvider(TestDbContext objDBContext)
{
_objDBContext = objDBContext;
}
public dynamic GetDataDetails(ReportDTO filters)
{
var response = new TestReponseExcelDto();
var ds = new DataSet();
using (var connection = new SqlConnection(_objDBContext.Database.GetDbConnection().ConnectionString))
{
connection.Open();
using (var command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[CR].[LoadProcedureDetailPopup]";
using (var sda = new SqlDataAdapter())
{
sda.SelectCommand = command;
sda.Fill(ds);
}
}
connection.Close();
}
if (ds.Tables.Count > 0)
{
response.Data = GetData(ds.Tables[0]);
response.TotalEngagements = response.Data.Select(d => d.TestReviewId).Distinct().Count();
}
return response;
}
}
In the above code while debugging I found error: Login failed for user ''. just after the control passes the code snippet connection.Open();. Even though the AccessToken was setup at the constructor within the TestDbContext , in this case I noticed that it is assigned with null value.
I added the below code before opening the connection and it started working fine as expected:
connection.AccessToken = ((SqlConnection)_objDBContext.Database.GetDbConnection()).AccessToken;
Even though my fix is solving the issue, I wanted to know whether it is correct way of doing it or are there better ways to manage it.
Can anyone help me to resolve this issue?

Testing Batch SendAll ServiceStack

I am getting an error on SendAll in a unittest
This works fine...
using (var service = HostContext.ResolveService<DeviceService>(authenticatedRequest))
{
service.Put(new AddConfig { ConfigName = key.KeyName, ConfigValue = key.Value, DeviceId = 0 });
}}
ServiceStack.WebServiceException: 'The operation 'AddConfig[]' does not exist for this service'
//DeviceConfig
/// <summary>
/// To insert new Config
/// </summary>
/// <returns> New row Id or -1 on error</returns>
public long Any(AddConfig request)
{
try
{
//convert request to model
var perm = request.ConvertTo<DeviceConfig>();
//log user
perm.AuditUserId = UserAuth.Id;
//insert data
var insert = Db.Insert(perm, selectIdentity:true);
//log inserted data
LogInfo(typeof(DeviceConfig), perm, LogAction.Insert);
return insert;
}
//on error log error and data
catch (Exception e)
{
Log.Error(e);
}
return -1;
}
[Route("/Config", "PUT")]
public class AddConfig : IReturn<long>
{
public int DeviceId { get; set; }
public string ConfigName { get; set; }
public string ConfigValue { get; set; }
}
public const string TestingUrl = "http://localhost:5123/";
public void DeviceX400Test(string deviceTemaplateFile)
{
//Resolve auto-wired service
WireUpService<DeviceService>();
var requests = new[]
{
new AddConfig { ConfigName = "Foo" },
new AddConfig { ConfigName = "Bar" },
new AddConfig { ConfigName = "Baz" },
};
var client = new JsonServiceClient(TestingUrl);
var deviceConfigs = client.SendAll(requests);
}
MY ServiceBase for Unit Testting that builds from my .netcore appsettings.Json file
public abstract class ServiceTestBase: IDisposable
{
//private readonly ServiceStackHost appHost;
public BasicRequest authenticatedRequest;
public const string TestingUrl = "http://localhost:5123/";
public SeflHostedAppHost apphost;
public ServiceTestBase()
{
var licenseKeyText = "********************************";
Licensing.RegisterLicense(licenseKeyText);
apphost = (SeflHostedAppHost) new SeflHostedAppHost()
.Init()
.Start(TestingUrl);
//regsiter a test user
apphost.Container.Register<IAuthSession>(c => new AuthUserSession { FirstName = "test", IsAuthenticated = true });
}
public void WireUpService<T>() where T : class
{
//var service = apphost.Container.Resolve<T>(); //Resolve auto-wired service
apphost.Container.AddTransient<T>();
authenticatedRequest = new BasicRequest
{
Items = {
[Keywords.Session] = new AuthUserSession { FirstName = "test" , UserAuthId="1", IsAuthenticated = true}
}
};
}
public virtual void Dispose()
{
apphost.Dispose();
}
}
//Create your ServiceStack AppHost with only the dependencies your tests need
/// <summary>
/// This class may need updates to match what is in the mvc.service apphost.cs
/// </summary>
public class SeflHostedAppHost : AppSelfHostBase
{
public IConfigurationRoot Configuration { get; set; }
public SeflHostedAppHost() : base("Customer REST Example", typeof(StartupService).Assembly) { }
public override void Configure(Container container)
{
var file = Path.GetFullPath(#"../../../../cbw.services");
var builder = new ConfigurationBuilder().SetBasePath(file).AddJsonFile("appsettings.json").AddJsonFile("appsettings.LocalSQLServer.json", optional: true);
Configuration = builder.Build();
var sqlString = Configuration["ConnectionString"];
RegisterServiceStack();
//container.Register<ServiceStack.Data.IDbConnectionFactory>(new OrmLiteConnectionFactory(sqlString,SqlServerDialect.Provider));
container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider));
container.RegisterAutoWired<DatabaseInitService>();
var service = container.Resolve<DatabaseInitService>();
container.Register<IAuthRepository>(c =>
new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
{
UseDistinctRoleTables = true,
});
container.Resolve<IAuthRepository>().InitSchema();
var authRepo = (OrmLiteAuthRepository)container.Resolve<IAuthRepository>();
service.ResetDatabase();
SessionService.ResetUsers(authRepo);
service.InitializeTablesAndData();
//Logging
LogManager.LogFactory = new SerilogFactory(new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Destructure.UsingAttributes()
.CreateLogger());
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
Serilog.Debugging.SelfLog.Enable(Console.Error);
ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
//ILog Log = LogManager.GetLogger(typeof(StartupService));
log.InfoFormat("Applicaiton Starting {Date}", DateTime.Now);
}
public void RegisterServiceStack()
{
var licenseKeyText = "****************************";
Licensing.RegisterLicense(licenseKeyText);
}
}
My Xunit Test
public class DeviceTemplateTest : ServiceTestBase
{
//Post Data
//Device Sends State.XML
[Theory]
[InlineData("C:\\DeviceTemplate.txt")]
public void DeviceX400Test(string deviceTemaplateFile)
{
//Resolve auto-wired service
WireUpService<DeviceService>();
var parser = new FileIniDataParser();
IniData data = parser.ReadFile(deviceTemaplateFile);
List<AddConfig> batch = new List<AddConfig>();
//Iterate through all the sections
foreach (SectionData section in data.Sections)
{
Console.WriteLine("[" + section.SectionName + "]");
//Iterate through all the keys in the current section
//printing the values
foreach (KeyData key in section.Keys)
{
batch.Add(new AddConfig { ConfigName = key.KeyName, ConfigValue = key.Value, DeviceId = 0 });
// using (var service = HostContext.ResolveService<DeviceService>(authenticatedRequest))
//{
// service.Any(new AddConfig { ConfigName = key.KeyName, ConfigValue = key.Value, DeviceId = 0 });
//}
}
}
var client = new JsonServiceClient(TestingUrl);
var deviceConfigs = client.SendAll(batch.ToArray());
}
}
Firstly, you should never return value types in Services, your Request DTO says it returns a DeviceConfig Response Type DTO:
public class AddConfig : IReturn<DeviceConfig> { ... }
Which your Service should be returning instead.
I'm unclear how this can work or compile:
using (var service = HostContext.ResolveService<DeviceService>(authenticatedRequest))
{
service.SendAll(new AddConfig {
ConfigName = key.KeyName, ConfigValue = key.Value, DeviceId = 0
});
}
Since it's calling methods on the DeviceService Service class directly and there is no SendAll() method on the Service class (or in your example), were you using the Service Gateway instead?
I can't tell what the issue is from here without seeing the full source code and being able to repro the issue but it sounds like AddConfig is not recognized as a Service, is it appearing in the /metadata page? If not do you have it a class that inherits Service?
Otherwise if you can post a minimal repro on GitHub, I'll be able to identify the issue.

MVC Repository, Unity, Identity Error

I'm starting a new project using MVC 5, Identity 2.x, Unity, and Dapper. I'm using the standard EF functionality for Identity but using Dapper for the rest of the DB access. I'm using a Repository Pattern for all my (non-Identity) DB calls.
I'm fairly new to Unity and Dapper but keep gettin a "Object reference not set to an instance of an object." error whenever I make a call to the DB interface in the Account Controller line from below:
var result = _companyaccountrepository.AddToCompanyUsers(model);
Can anyone point out what I'm doing wrong? Thanks in advance.
Account Controller
private ICompanyAccountRepository _companyaccountrepository { get; set; }
public ICompanyAccountRepository companyaccountrepository
{
get { return _companyaccountrepository ?? (_companyaccountrepository = new CompanyAccountRepository()); }
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
private ApplicationSignInManager _signInManager;
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ICompanyAccountRepository companyaccountrepository)
{
UserManager = userManager;
SignInManager = signInManager;
_companyaccountrepository = companyaccountrepository;
}
...
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SignUp(RegisterUserAndCompanyViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
user.FirstName = model.FirstName;
user.LastName = model.LastName;
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
var result = _companyaccountrepository.AddToCompanyUsers(model); //*** THIS IS WHERE THE PROBLEM OCCURS ****
return RedirectToAction("Confirmation");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Interface/Dapper SQL (dummy code to make it simple)
public interface ICompanyAccountRepository
{
CompanyUser AddToCompanyUsers(RegisterUserAndCompanyViewModel user);
}
public class CompanyAccountRepository : ICompanyAccountRepository
{
private string dbconn = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
public bool AddToCompanyUsers(RegisterUserAndCompanyViewModel user);
{
using (SqlConnection cn = new SqlConnection(dbconn))
{
cn.Open();
cn.Insert(new CompanyUser() { CompanyId = user.companyid, UserId = user.id });
cn.Close();
}
return true;
}
}
Unity.Config
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
//12-1-16 Need this for Identity
container.RegisterType<ApplicationDbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
container.RegisterType<EmailService>();
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(ApplicationDbContext)));
container.RegisterType<AccountController>(
new InjectionConstructor(typeof(ApplicationUserManager), typeof(ApplicationSignInManager), typeof(ICompanyAccountRepository)));
container.RegisterType<AccountController>(
new InjectionConstructor());
//Identity / Unity stuff below to fix No IUserToken Issue - http://stackoverflow.com/questions/24731426/register-iauthenticationmanager-with-unity
//container.RegisterType<DbContext, ApplicationDbContext>(
// new HierarchicalLifetimeManager());
container.RegisterType<UserManager<ApplicationUser>>(
new HierarchicalLifetimeManager());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new HierarchicalLifetimeManager());
container.RegisterType<ICompanyAccountRepository, CompanyAccountRepository>();
}
Thanks again for any suggestions.
NOTE: If I add instantiate the repository just before the AddToCompanyUsers call (below), it works fine. However, this breaks Unity/IOC
_companyaccountrepository= new CompanyAccountRepository();
var result = _companyaccountrepository.AddToCompanyUsers(model);
You can try it like this:
(this should fix your repository error. As for your userManager and signInManager, I believe you can improve how they are configured as well, but that will take to take a look on your startup.auth and your ApplicationDbContext and with all the Identity configuration)
Account Controller
private readonly ICompanyAccountRepository _companyaccountrepository;// { get; set; } -- remove the getter and setter here
//remove this
// public ICompanyAccountRepository companyaccountrepository
// {
// get { return _companyaccountrepository ?? (_companyaccountrepository = new CompanyAccountRepository()); }
// }
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
private ApplicationSignInManager _signInManager;
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
//I think you can remove the parameterless constructor as well
//public AccountController()
//{
//
//}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ICompanyAccountRepository companyaccountrepository)
{
UserManager = userManager;
SignInManager = signInManager;
_companyaccountrepository = companyaccountrepository;
}
...
EDIT
Change your constructor to:
public AccountController(ICompanyAccountRepository companyaccountrepository)
{
_companyaccountrepository = companyaccountrepository;
}

How to register IUserAuthRepository when the OrmLiteConnectionFactory is using Named Connections

Here is the appHost configuration code:
OrmLiteConfig.DialectProvider = PostgreSQLDialectProvider.Instance;
var dbFactory = new OrmLiteConnectionFactory();
dbFactory.RegisterConnection("NamedKeyConnOne", new OrmLiteConnectionFactory("ConnOne"))
{
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
dbFactory.RegisterConnection("NamedKeyConnTwo", new OrmLiteConnectionFactory("ConnTwo")
{
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
container.Register<IDbConnectionFactory>(dbFactory);
and here is the authentication portion:
container.Register<IUserAuthRepository>(c => new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())); //Authentication and authorization
container.Resolve<IUserAuthRepository>().InitSchema();
So my question is "How do you go about passing the correct IDbConnectionFactory when there is no default connection string?"
Thank you,
Stephen
You can't inject a named IDbConnection connection but you can resolve it from the IDbConnectionFactory which you can access from your services like:
public class MyServices : Service
{
public IDbConnectionFactory DbFactory { get; set; }
public object Any(Request request)
{
using (var db = DbFactory.OpenDbConnection("NamedKeyConnOne"))
{
//...
}
}
}

Set public properties in ServiceStack web services

I am trying to write unit tests for ServiceStack services using Sqlite. Since Sqlite doesn't support stored procedures, I've created public property of type 'ServiceCommand' that takes command text and command type as inputs. By default it is configured to run stored procedure and while writing unit test cases, I am re-assigning the SelectCommand property to sql query against sqlite before calling Any() method as below. All test cases work fine.
var request = new CustomerRequest() { name = "alfki" };
var service = new CustomerService(InMemoryTestDatabase.OpenDbConnection());
service.SelectCommand = new ServiceCommand() { SQL = "SELECT * FROM customers where customerid = {0}" };
var result = service.Any(request);
But per this thread, public properties of CustomerService are set to null by IOC while resolving the references thus SelectCommand is null in Any() method resulting in object reference error. By setting the property as protected, private, internal or static, I will not be able to run unit tests.
public class CustomerService : Service
{
private readonly IDbConnection _dbConnection;
public ServiceCommand SelectCommand {get;set;}
public CustomerService(IDBConnection dbConnection)
{
_dbConnection = dbConnection; //injected successfully
SelectCommand = new ServiceCommand(){ SQL = "sp_getcustomers",
CommandType = CommandType.StoredProcedure};
}
public Customer Any(CustomerRequest request)
{
//Select command is not accessible here.
}
}
[Route("/customers")]
public class CustomerRequest
{
public string name { get; set; }
}
ServiceCommand
public class ServiceCommand
{
public string SQL { get; set; }
public CommandType CommandType { get; set; }
public ServiceCommand()
{
CommandType = CommandType.Text;
}
}
To be able to run test cases and service as well, I've modified Any() method to instantiate ServiceCommand if it is null. I would like to know if this is the way to go or any better alternatives.
public class CustomerService : Service
{
private readonly IDbConnection _dbConnection; // injected successfully
public ServiceCommand SelectCommand {get;set;}
public CustomerService(IDBConnection dbConnection)
{
_dbConnection = dbConnection; //injected successfully
}
public Customer Any(CustomerRequest request)
{
SelectCommand = SelectCommand ?? new ServiceCommand() { SQL = "sp_getCustomers",CommandType = CommandType.StoredProcedure };
}
}
Since ServiceStack services will inject registered IOC properties for all public properties it will override the value you set in the constructor so you can't make it a public property without registering it in the IOC since it will be overridden to null.
Given this, some possible options are:
Make it a public field
public class CustomerService : Service
{
public ServiceCommand SelectCommand = new ServiceCommand {
SQL = "sp_getcustomers",
CommandType = CommandType.StoredProcedure };
...
}
Inject a read-only property with a setter
service.SetSelectCommand(
new ServiceCommand { SQL = "SELECT * FROM customers where customerid = {0}" });
Register it in your IOC and specify it in your constructor
container.Register(new ServiceCommand {
SQL = "sp_getcustomers",
CommandType = CommandType.StoredProcedure });
and change constructor to:
public CustomerService(IDBConnection dbConnection, ServiceCommand serviceCommand)
{
_dbConnection = dbConnection;
_serviceCommand = serviceCommand;
}

Resources