Call a class constructor with parameter inside the parent class - c#

I have this class as you can see :
public class mybaseclass
{
public string token = "";
private readonly HttpContextAccessor iHTTP;
public mybaseclass([FromServices]HttpContextAccessor IHTTP)
{
//this.httpContext = httpContext;
iHTTP = IHTTP;
}
public mybaseclass()
{
}
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
{
// var t = null;
try
{
// iHTTP.HttpContext.Request.Cookies[key]
var t = iHTTP.HttpContext.Request.Cookies["Authorization"];
if (t == null)
{
token = t;
}
}
catch(Exception aaa)
{
}
var msg = new HttpRequestMessage();
// SET THE BEARER AUTH TOKEN
msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
return Task.FromResult(msg);
}
}
And this class is called in this class :
public partial class Default1Client : mybaseclass, IDefault1Client
{
private string _baseUrl = "";
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public Default1Client(string baseUrl)
{
BaseUrl = baseUrl;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
// other part of code
}
My problem is when I call Default1Client,the class calls the mybaseclass constructor that doesn't have any parameters ,but I need the constructor with httpcontextaccessorto be called

Modify your constructor to pass it through:
public Default1Client(string baseUrl, HttpContextAccessor contextAccessor) : base(contextAccessor)
{
//etc....
And populate it when you register:
services.AddScoped<IDefault1Client>(provider => { return new Default1Client
(
"localhost:44381",
provider.GetService(typeof(HttpContextAccessor)) as HttpContextAccesor
)});

In this case you want to specify the parameters, we can inject them using a function that will get the httpContextAccessor from your baseurl:
// you can use a function call to get the accessor
public Default1Client(string baseUrl) : mybaseclass(getHttpContextAccessor(baseUrl))
If you pass it the parameters that will correspond to httpcontextaccessor the constructor which takes parameters will be called.

Related

Dependency Injection - directory inject one class in another class

I have a MailRepository class with the following structire:
public class MailRepository : IMailRepository
{
public MailRepository()
{
}
public async Task SendMail(string subject, string content, string recipientAddress)
{
}
}
I also have a LocalizationReposiory class with the following code:
public class LocalizationRepository : ILocalizationRepository
{
private readonly IStringLocalizer<LocalizationRepository> _localizer = null;
public LocalizationRepository(IStringLocalizer<LocalizationRepository> localizer)
{
_localizer = localizer;
}
public string TranslateSetting(string settingName, params string[] additionalParams)
{
return _localizer.GetString(settingName, additionalParams);
}
}
This is how I call SendMail method in MailRepository from a class:
var subject = _localizationRepository.TranslateSetting("Subject");
var content = _localizationRepository.TranslateSetting("Body");
await _mailRepository.SendMail(subject, content, "xyz#yahoo.com");
This is how dependency injection in startup looks like:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IMailRepository>(services => new MailRepository());
builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; });
builder.Services.Configure<RequestLocalizationOptions>(opts =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US")
};
opts.DefaultRequestCulture = new RequestCulture("en-US");
opts.SupportedCultures = supportedCultures;
opts.SupportedUICultures = supportedCultures;
});
builder.Services.AddSingleton<ILocalizationRepository, LocalizationRepository>();
}
}
Is there a way to inject the LocalizationRepository directly into MailRepository in order to avoid duplicating the following lines of code in multiple classes?
var subject = _localizationRepository.TranslateSetting("Subject");
var content = _localizationRepository.TranslateSetting("Body");
await _mailRepository.SendMail(subject, c,ontent "xyz#yahoo.com");
Inject ILocalizationRepository into MailRepository
public MailRepository(ILocalizationRepository localizationRepo)
{
//set private var
}
and then change how you register it in service provider
builder.Services.AddSingleton<IMailRepository, MailRepository>();

DbContextOptions parameters ASP.net core

I am getting ArgumentNullException: Value cannot be null. Parameter name: options
I think it's because of my default constructor that I get null but If I comment out the constructor then the new instance of a class needs parameters which I don't know what to give in. HELP PLEASE
public class OvertimeRequestBusiness
{
public static OvertimeRequestBusiness Instance { get; } = new OvertimeRequestBusiness();
private readonly DbContextOptions<DatabaseContext> _contextOptions;
//default ctor
public OvertimeRequestBusiness() : base() { }
public OvertimeRequestBusiness(DbContextOptions<DatabaseContext> contextOptions)
{
_contextOptions = contextOptions;
}
public async Task<List<User>> GetAllUsersAsync()
{
using (var ctx = new DatabaseContext(_contextOptions))
{
var query = ctx.Users;
var res = await query.ToListAsync();
return res;
}
}
}
In my controller
[Route("users")]
[HttpGet]
public async Task<List<User>> GetAllUsers()
{
return await OvertimeRequestBusiness.Instance.GetAllUsersAsync();
}
Here is how you can create DbContextOptions:
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
optionsBuilder.UseSqlServer("connection_string_here");
then DbContextOptions<DatabaseContext> will be available as optionsBuilder.Options. You can pass it to OvertimeRequestBusiness.ctor:
new OvertimeRequestBusiness(optionsBuilder.Options);

How should I get the Request in ApiController constructor?

Task
I have a DataMapper class that I use to map data into custom representations for my web api's mobile client.
public class DataMapper
{
public static string Role { get; set; }
public static RoleReturnModel Create(IdentityRole appRole)
{
return new RoleReturnModel
{
Id = appRole.Id,
Name = appRole.Name
};
}
public static CountryReturnModel Create(Country country)
{
return new CountryReturnModel
{
Id = country.Id,
Name = country.Name,
CityList = country.Cities.Select(city => DataMapper.Create(city))
};
}
public static CityReturnModel Create(City city)
{
return new CityReturnModel
{
Id = city.Id,
Name = city.Name,
};
}
}
The first property as you can see is called Role. I need to populate that with whichever role is accessing my web method. This is so because at times I need conditional mapping to return role specific data representations to the client.
Problem
I thought the best place to do DataMapper.Role = CurrentRole would be in the constructor of my ApiController
public class BaseApiController : ApiController
{
private ModelFactory _modelFactory;
private ApplicationUserManager _AppUserManager = null;
private ApplicationRoleManager _AppRoleManager = null;
protected BaseApiController()
{
//Request is null here
DataMapper.Role = Request.GetOwinContext().GetUserManager<ApplicationRoleManager>().FindById(User.Identity.GetUserId()).Name;
}
This however doesn't work . The Request object is null in the constructor. It only gets filled in my actual web method
public class UsersController : BaseApiController
{
IUserRepository UserRepository;
public UsersController() // will use ninject for constructor injection
{
UserRepository = new UserRepository();
}
[Route("profile")]
public IHttpActionResult GetUser()
{
//Request is available here
}
I am a webapi noobie. Need pointers to this problem.
The request is not available as yet in the constructor. You can only access it in an action/method after the controller has already been initialized.
public class BaseApiController : ApiController {
private ModelFactory _modelFactory;
private ApplicationUserManager _AppUserManager = null;
private ApplicationRoleManager _AppRoleManager = null;
protected string GetRole() {
return Request.GetOwinContext()
.GetUserManager<ApplicationRoleManager>()
.FindById(User.Identity.GetUserId()).Name;
}
And accessed
public class UsersController : BaseApiController {
IUserRepository UserRepository;
public UsersController() // will use ninject for constructor injection
{
UserRepository = new UserRepository();
}
[Route("profile")]
public IHttpActionResult GetUser()
{
//Request is available here
var role = GetRole();
}
Or consider extracting that out into an extension method so that it can be reused
var role = this.GetUserRole();
Where
public static string GetUserRole(this ApiController controller) {
var request = controller.Request;
var user = controller.User
return request.GetOwinContext()
.GetUserManager<ApplicationRoleManager>()
.FindById(user.Identity.GetUserId()).Name;
}

Getting error with aspnet core 2.1 action filter due to missing suitable constructor

I have made a claims filter
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, ClaimRoles claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue.ToString()) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var headers = context.HttpContext.Request.Headers;
var tokenSuccess = headers.TryGetValue("Token", out var token);
var emailSuccess = headers.TryGetValue("Email", out var email);
var deviceNameSuccess = headers.TryGetValue("DeviceName", out var deviceName);
if (tokenSuccess && emailSuccess && deviceNameSuccess)
{
var accountLogic = context.HttpContext.RequestServices.GetService<IAccountLogic>();
var hasClaim = accountLogic.ValidateLogin(email, token, deviceName).Result.Success;
if (!hasClaim)
{
context.HttpContext.ForbidAsync();
}
}
else
{
context.HttpContext.ForbidAsync();
}
}
}
I have registered the filter in my startup
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ConnectionStringsSettings>(Configuration.GetSection("ConnectionStrings"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<ClaimRequirementFilter>();
But I get this error when I navigate to an action that uses the filter
[HttpPost]
[ClaimRequirement("Permission", ClaimRoles.Admin)]
public async Task ResetLeaderboard()
InvalidOperationException: A suitable constructor for type 'Foosball.Logic.ClaimRequirementFilter' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor
github: https://github.com/Mech0z/Foosball/tree/core2.1/Foosball
As your code has
Arguments = new object[] {new Claim(claimType, claimValue.ToString()) };
you need to add the following constructor:
public ClaimRequirementFilter(Claim claim)
{
}
That is because the internal constructor resolving logic uses TypeFilterAttribute.Argument property to decide what constructor to use for instantiation.

log4net.LogicalThreadContext.Properties does not work correctly with OwinMiddleware

I am trying to use log4net.LogicalThreadContext to store some data in the OwinMiddleware, so i can log it later in the ApiController, but it doesn't seem to work. The data stored in log4net.LogicalThreadContext doesn't seems to make it available in the ApiController. Here is my code snippet:
Created the ApiMiddleWare in order to inject some log data to LogicalThreadContext.Properties["logdata"]:
public class ApiMiddleWare : OwinMiddleware
{
public ApiMiddleWare(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var loggers = new ConcurrentDictionary<string, object>();
if (!loggers.ContainsKey("CorellationId"))
{
var correlationId = new[] {Guid.NewGuid().ToString().Replace("-", "")};
loggers.TryAdd("CorellationId", correlationId[0]);
}
if (context.Request.Path.HasValue)
{
loggers.TryAdd("Route", context.Request.Uri.AbsoluteUri);
}
LogicalThreadContext.Properties["logdata"] = loggers;
await Next.Invoke(context);
}
}
Then ApiMiddleWare will be used in Startup.cs in ServiceHost as below:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<ApiMiddleWare>();
Log.Configure();
}
}
I created a custom RollingFileAppeanderEx to capture log data that was assigned in the middleware and log it:
public class RollingFileAppenderEx: RollingFileAppender
{
protected static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat
};
protected override void Append(LoggingEvent loggingEvent)
{
if (FilterEvent(loggingEvent))
{
var logdata = loggingEvent.GetLoggingEventData();
logdata.Message = GetLogData(loggingEvent);
loggingEvent = new LoggingEvent(loggingEvent.GetType(), loggingEvent.Repository, logdata, loggingEvent.Fix);
base.Append(loggingEvent);
}
}
protected string GetLogData(LoggingEvent logEvent)
{
IDictionary<string, object> logData = new Dictionary<string, object>();
var logD = logEvent.Properties["logdata"] as ConcurrentDictionary<string, object>;
if logD != null)
{
foreach (var log in logD)
{
logData.Add(log.Key, log.Value);
}
}
logData.Add("Message", logObject.Message);
var logString = JsonConvert.SerializeObject(logData, JsonSettings);
return logString;
}
}
From The ApiController, call Info function to log:
public class TestsController : ApiController
{
private static readonly ILogger Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public string Get(int id)
{
Log.Info("Something");
return id;
}
}
Here is my problem: Only "Something" was written to the log. However, CorellationId and Route were not. Debugging through the code, I found that "logEvent.Properties["logdata"] as ConcurrentDictionary" returned nullable value in the RollingFileAppenderEx. So i have a theory: it seems that TestsController class is not in the same thread or not a child thread from ApiMiddleWare. Therefore, data stored in LogicalThreadContext does not propagate all the way.
If anyone can help to see if there is a way to do this, or maybe there is a bug in my code. I would appreciate it. Thanks.
Maybe you have to call loggingEvent.GetLoggingEventData()?
I had:
public abstract class AwsVerboseLogsAppender : AppenderSkeleton
{
// ...
protected override void Append(LoggingEvent loggingEvent)
{
// ...
}
// ...
}
// ...
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config");
var fileinfo = new FileInfo(path);
XmlConfigurator.Configure(fileinfo);
var log = LogManager.GetLogger(GetType());
LogicalThreadContext.Properties["Test"] = "MyValue";
log.Debug("test");
And loggingEvent.Properties was empty inside Append.
However if I called loggingEvent.GetLoggingEventData(), then "Test" and MyValue showed up inside loggingEvent.Properties. So maybe you have to call that method.

Categories

Resources