My code might be bit lengthy but easy to understand as I have tried my best to explain it, so please bare with me.
I have a controller class as:
public class QueryController : ControllerBase
{
private readonly ILogger<QueryController> log;
public bool ISReadCol { get; set; } //Focus on this part
public QueryController(ILogger<QueryController> logger)
{
this.log = logger;
}
[HttpGet("/api/v2/{database}/Tables/{table}")]
public async Task<IActionResult> GetColFields(
[FromRoute] string database,
[FromRoute] string table,
CancellationToken cancel)
{
//some code
ISReadCol = true; //setting property to true
return await GetQueryResult(database);
}
private async Task<IActionResult> GetQueryResult(string database)
{
//some code
return new QueryResult(pool, log); //[1]
}
}
Now, I want to access the property "ISReadCol" in the "QueryResult" class.
The "QueryResult.cs" is as follows:
class QueryResult : ActionResult
{
private readonly ILogger log;
private readonly ConnectionPoolEntry poolEntry;
public QueryResult(ConnectionPoolEntry poolEntry, ILogger log)
{
this.log = log;
this.poolEntry = poolEntry;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
**//HOW CAN I ACCESS THE "ISReadCol" property here???.**
}
}
IF I pass "QueryController" instance in "QueryResult" constructor such as:
private readonly QueryController QR;
public QueryResult(ConnectionPoolEntry poolEntry, ILogger log, QueryController QR)
{
this.log = log;
this.poolEntry = poolEntry;
this.QR = QR;
}
and then QR.ISReadCol, but that doesn't work too as [1] call need to be updated too.
Just passed 'ISReadCol' as argument and modified the function calls accordingly
Related
I'm very new to ASP.NET Web API and I'm trying to use Entity Framework Core's Dependency Injection to POST data to the API Controller using MediatR pattern. But every time I run my code and it opens Swagger UI, I get an error 500 response saying
Unable to cast object of type 'AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult,S3E1.Repository.CartItemRepository+<Createitem>d__5]' to type 'System.Threading.Tasks.Task1[S3E1.Entities.CartItemEntity]'.
First, I added Dependency Injections to Program.cs
//Dependency Injection
builder.Services.AddDbContext<AppDataContext>(contextOptions => contextOptions.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")
));
//Connection
builder.Services.AddSingleton<DataConnectionContext>();
These are the classes.
AppDataContext.cs
public class AppDataContext : DbContext
{
public AppDataContext(DbContextOptions<AppDataContext> contextOptions) : base(contextOptions) { }
public DbSet<CartItemEntity> CartItems { get; set; }
public DbSet<OrderEntity> Orders { get; set; }
public DbSet<UserEntity> Users{ get; set; }
}
DataConnectionContext.cs
public class DataConnectionContext
{
private readonly IConfiguration _configuration;
private readonly string _connectionString;
public DataConnectionContext(IConfiguration configuration)
{
_configuration = configuration;
_connectionString = _configuration.GetConnectionString("DefaultConnection");
}
public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
}
Next is making a repository which holds the interface that has the create method.
public interface ICartItemRepository
{
//public Task<IEnumerable<CartItemEntity>> GetCartItems();
//public Task<CartItemEntity> GetCartItemEntity(Guid id);
public Task Createitem(CartItemEntity itemEntity);
}
Then a class that inherits the interface and calls the dependency constructors
public class CartItemRepository : ICartItemRepository
{
private readonly DataConnectionContext _connectionContext;
private readonly AppDataContext _appDataContext;
public CartItemRepository(DataConnectionContext connectionContext, AppDataContext appDataContext)
{
_connectionContext = connectionContext;
_appDataContext = appDataContext;
}
public async Task Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
await _appDataContext.CartItems.ToListAsync();
}
}
Next is a command for POST request MediatR pattern
public record AddCartItemCommand(CartItemEntity cartItem) : IRequest<CartItemEntity>;
and a Handler which manages and returns the method createitem
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
private readonly ICartItemRepository _cartItemRepository;
public AddItemsHandler(ICartItemRepository cartItemRepository) => _cartItemRepository = cartItemRepository;
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await (Task<CartItemEntity>) _cartItemRepository.Createitem(request.cartItem);
}
}
and lastly, in the controller
[Route("api/cart-items")]
[ApiController]
public class CartItemsController : ControllerBase
{
private ISender _sender;
public CartItemsController(ISender sender) => _sender = sender;
[HttpPost]
public async Task<CartItemEntity> Post(CartItemEntity cartItemEntity)
{
return await _sender.Send(new AddCartItemCommand(cartItemEntity));
}
}
I tried modifying the return object in the handler but every time I change anything it always get the error squiggly line, so I just casted the (Task) after the await. Is this where I went wrong? Thank you for any answers.
The exception is clear. You can't cast a VoidTaskResult to Task<CartItemEntity>.
To solve the problem:
In ICartItemRepository, modify the return type for Createitem as Task<CartItemEntity>.
In CartItemRepository, implement Createitem method from the ICartItemRepository interface. Return the inserted itemEntity in the method.
Since you have implemented Task<CartItemEntity> Createitem(CartItemEntity itemEntity) in the ICartItemRepository interface, the casting to (Task<CartItemEntity>) is no longer needed, and suggested to be removed.
public interface ICartItemRepository
{
...
public Task<CartItemEntity> Createitem(CartItemEntity itemEntity);
}
public class CartItemRepository : ICartItemRepository
{
...
public async Task<CartItemEntity> Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
return itemEntity;
}
}
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
...
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await _cartItemRepository.Createitem(request.cartItem);
}
}
I am creating an app in .net core and I'm trying to output a log.
I was able to log output from the controller and I also want to log output from the model, which is shown below.
Unfortunately, I don't know what to pass as arguments to the constructor. My guess is the same Ilogger<controller> as the controller, but I would like to know if there is a correct way.
Thanks in advance.
Controller:
public class SampleController : Controller {
public readonly ILogger<SampleController > _logger;
public SampleController (ILogger<SampleController > logger)
{
_logger = logger;
}
[HttpGet("[action]")]
public string FindSample()
{
_logger.LogInformation("FindSample Start");
// ***** Is it necessary to carry over the log of the sample controller to a much lower DAO from here? *****
var model = new SampleModel(_logger);
var result = model.Find();
_logger.LogInformation("FindSample End");
return result;
}
}
Model:
public class SampleModel
{
public readonly ILogger<SampleController> _logger;
public SampleModel(ILogger<SampleController> logger)
{
_logger = logger;
}
public string Find()
{
_logger.LogInformation("FindModel Start");
var dao = new SampleDao(_logger);
var code = dao.GetCode();
_logger.LogInformation("FindModel End");
return code;
}
}
Dao:
public class SampleDao
{
public readonly ILogger<SampleController> _logger;
public SampleContext SampleContext;
// ***** Should I pass it as an argument forever? *****
public SampleDao(ILogger<SampleController> logger)
{
_logger = logger;
if (SampleContext == null)
{
SampleContext = new SampleContext();
}
}
public string GetCode()
{
_logger.LogInformation("GetCode Start");
var code = SampleContext.SampleTable.FirstOrDefault().code;
_logger.LogInformation("GetCode End");
return code;
}
}
You should pass as a generic argument the class in which the logger is being injected. So you should use:
In your Dao
ILogger<SampleDao> logger
In your Model
ILogger<SampleModel> logger
For more information you could check:
Logging in ASP.NET core
I am trying to get client IP and compare values with the configuration. If it matches need to return true/false. How do I make this variable accessible to web application? I am new to .NET core. thanks
I have followed this article to create middleware class but not sure how to pass variable from this context.
https://learn.microsoft.com/en-us/aspnet/core/security/ip-safelist?view=aspnetcore-2.2
public class SafeListMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SafeListMiddleware> _logger;
private readonly string _adminSafeList;
public SafeListMiddleware(
RequestDelegate next,
ILogger<SafeListMiddleware> logger,
string adminSafeList)
{
_adminSafeList = adminSafeList;
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Method != "GET")
{
var remoteIp = context.Connection.RemoteIpAddress;
string[] ip = _adminSafeList.Split(';');
var bytes = remoteIp.GetAddressBytes();
var match = false;
foreach (var address in ip)
{
var testIp = IPAddress.Parse(address);
var rangeA = IPAddressRange.Parse(address);
if(rangeA.Contains(remoteIp))
{
match = true;
break;
}
}
}
await _next.Invoke(context);
}
}
}
I would create an interface such as:
public interface IIPChecker
{
bool IsSafe(IPAddress remoteIpAddress);
}
with an implementation:
public class IPChecker : IIPChecker
{
private readonly IPAddress[] _safeList;
public IPChecker(string safeList)
{
var _safeList = safeList
.Split(';')
.Select(IPAddress.Parse)
.ToArray();
}
public bool IsSafe(IPAddress remoteIpAddress)
{
return _safeList.Contains(remoteIpAddress);
}
}
and inject it in the controllers that need it:
public class ValuesController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public ValuesController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var isValid = _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
.....
}
}
If you need this information in all controllers, you can change them to inherit from something like
public class IpCheckController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public IpCheckController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
private bool IsSafe => _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
}
To get the client RemoteIp and compare with the configuration values. you have to first define Http Accessor in the Startup file like below.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and then in the Middleware access the RemoteIp using below code and compare the value.
var remoteIp = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();
I am having issues where my DbContext is being disposed of early. It is only ever apparent when calling any of the *Async methods, such as ToListAsync() - if i call any of the syncronous methods everything is fine.
I can't figure out what I'm doing wrong.
Any advise please?
Here is as much of the code as i believe is needed.
The DbContext and its interface
public interface IMyDbContext
{
DbSet<MyModel> MyModels { get; set; }
}
public class MyDbContext : DbContext, IMyDbContext
{
public DbSet<MyModel> MyModels { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new MyModelConfig());
}
}
A Repository using this DbContext
public class MyModelRepository : IMyModelRepository
{
private readonly IMyDbContext _dbContext;
private string _baseSql = "Some SQL here ";
public MyModelRepository(IMyDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IList<MyModel>> GetAllAsync(Paging paging, Permission permission)
{
if (permission == null)
throw new ArgumentNullException("permission");
string sql = ApplyFilter(_baseSql, permission);
try
{
// THIS FAILS
return await _dbContext.MyModels.FromSql(sql).Skip(paging.Skip).Take(paging.Take).ToListAsync();
// THIS FAILS
return await _dbContext.MyModels.FromSql(sql).ToListAsync();
// THIS WORKS
return await _dbContext.MyModels.FromSql(sql).ToList();
}
catch (Exception e)
{
throw new InvalidOperationException("Could not retrieve data", e);
}
}
}
I'm calling the repo via a service that looks like this:
public class GetAllMyModelQuery : IGetAllMyModelQuery
{
private readonly IMyModelRepository _myModelRepository;
private readonly IPermissionService _permissionService;
private readonly ILogger _logger;
public GetAllAbsenceQuery(IMyModelRepository myModelRepository, IPermissionService permissionService, ILogger<GetAllMyModelQuery> logger)
{
_myModelRepository = myModelRepository;
_permissionService = permissionService;
_logger = logger;
}
public async Task<IList<Emp_AbsenceEtrac>> Execute(Paging paging)
{
if (_permissionService.Permission == null)
{
_logger.LogInformation("No permission to the requested resource");
return null;
}
// if external?
// call external repo
//TODO//
// else
return await _myModelRepository.GetAllAsync(paging, _permissionService.Permission);
}
}
This in turn is called by the controller
public class MyModelController : Controller
{
private readonly IQueryStore _queryStore;
public MyModelController(IQueryStore queryStore)
{
_queryStore = queryStore;
}
[HttpGet]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(BadRequestObjectResult), (int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Index([FromQuery] int offset = 0, [FromQuery] int limit = 25)
{
Paging paging = new Paging(offset, limit);
return Ok(_queryStore.GetAllMyModelQuery.Execute(paging));
}
}
Finally, it's all wired together in the startup:
services.AddScoped<IMyDbContext, MyDbContext>();
services.AddScoped<IMyModelRepository, MyModelRepository>();
// Everything else above is also added as scope..
services.AddDbContext<MyDbContext>(opts =>
{
opts.UseSqlServer(Configuration.GetConnectionString("MyDb"),
sqlServerOptions =>
{
sqlServerOptions.CommandTimeout(600);
// required to allow skip/take on sql server 2008
sqlServerOptions.UseRowNumberForPaging(true);
});
});
Is there anything jumping out that would cause my Async calls to result in a closed Db connection?
Error is:
You should await the GetAllMyModelQuery.Execute method in your Index controller action:
[HttpGet]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(BadRequestObjectResult), (int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Index([FromQuery] int offset = 0, [FromQuery] int limit = 25)
{
Paging paging = new Paging(offset, limit);
return Ok(await _queryStore.GetAllMyModelQuery.Execute(paging).ConfigureAwait(false));
}
(I am sure that I formatted the question badly, I would be happy to revise and fix depending on comments)
I have a static class and I am trying to improve the design with dependency injection. I don't necessarily want this class to be static anymore because I will be using .NET Core, which promotes dependency injection over static class situations.
The simplified code in .NET (not Core):
public static class Utils
{
public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];
public async static Task<bool> SendEmail(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", tokenKey);
try
{
await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
For ConfigurationManager.AppSettings (it does not exist in .NET Core), I am planning to use the method in this link: http://www.danylkoweb.com/Blog/no-configurationmanager-in-aspnet-core-GC
However, for converting this (SendMail) method into a dependency injection, I am quite lost. I have read many examples and articles and I understand the logic of dependency injection but I don't know how to convert this static class into a proper dependency injection. There are other methods in the same Utils class but this is the simplest one and I hope to figure out the others using this one.
An approach that I was thinking off was:
public interface ISendMail
{
FormSettings ConfigSettings { get; set; }
Task<bool> SendEmail(IOptions<FormSettings> settings, Email email);
}
and:
public class SendEmail : ISendMail
{
public async static Task<bool> SendEmail(IOptions<FormSettings> settings, Email email)
{
//do same things
}
}
but I am CLEARLY lost with this because it does not even make sense. Another approach that I was thinking of was:
public class SendEmail
{
FormSettings ConfigSettings { get; set; }
protected Email email = null;
public SendEmail(IOptions<FormSettings> settings, Email email)
{
ConfigSettings = settings.Value;
this.email = email;
}
public async static Task<bool> SendEmailAction()
{
//do same things with "email" and "ConfigSettings"
}
}
I know I am giving a lot of code here and I wasn't sure if I should ask about this in "Code Review" or something. My biggest concern is not the FormSettings part but implementing the functionality of SendEmail in a dependency injection format.
Shortly, how can I convert this "SendEmail" class into a format where I can use it with .NET Core, without having a static class? This particular method does not require change with .NET Core but my other methods do, that is why I am trying to get rid of the static class approach.
I can exclude the tokenUrl and tokenKey parts and simplify the problem if requested, I am just quite lost as to how to approach this situation.
What should do this class? Sending email, right? So interface:
public interface IEmailSender
{
Task<bool> Send(Email email);
}
How we can implement it? Like this:
public class MyEmailSenderOne : IEmailSender
{
public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];
public async Task<bool> Send(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", tokenKey);
try
{
await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
or
public class MyAnotherAwesomeEmailSender : IEmailSender
{
public async Task<bool> Send(Email email)
{
// send with different way
return true;
}
}
How we can inject this?
public class SomeClass
{
private IEmailSender _sender;
public SomeClass(IEmailSender sender)
{
_sender = sender;
}
public void Foo()
{
// do smth useful
_sender.Send(new Email());
}
}
UPD.
Because your email settings persistant (will not change during lifetime), and because this settings related ONLY to your implementation of IEMailSender, you should to inject them in your implementation. Just think about = why caller code (Controller) should know about how your implementation works?
So
public class MyEmailSenderOne : IEmailSender
{
private FormSettings _settings;
public MyEmailSenderOne(IOptions<FormSettings> settings)
{
_settings = settings.Value;
}
public async Task<bool> Send(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", _settings.tokenApiKey);
try
{
await http.PostAsync(_settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
And, controller now dint know about any settings for your implementation, and it looks like
public class CommunicationsController : Controller
{
private IEmailSender _sender;
public CommunicationsController(IEmailSender sender)
{
_sender = sender;
}
public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
{
...
request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) });
...
}
}
As you can see, controller is very clean now and you can easily change your implementation of IEmailSender to any other without changing Controller code. This is one of advantages of using DI.
Based on tym32167's answer, I was able to implement the IEmailSender functionality (finally). I will still choose his answer as the correct answer but this is how I implemented dependency injection.
Please read the link I provided in the question, if you'd like to know more about the IOptions and FormSettings class that I am using.
Here is the interface and the class:
public interface IEmailSender
{
Task<bool> SendEmail(Email email, FormSettings settings);
}
public class EmailSender : IEmailSender
{
FormSettings ConfigSettings { get; set; }
public async Task<bool> SendEmail(Email email, FormSettings settings)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", settings.tokenApiKey);
try
{
await http.PostAsync(settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
In controller injection:
public class CommunicationsController : Controller
{
private IEmailSender _sender;
private FormSettings ConfigSettings { get; set; }
public CommunicationsController(IEmailSender sender, IOptions<FormSettings> settings)
{
_sender = sender;
ConfigSettings = settings.Value;
}
public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
{
...
request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) }, ConfigSettings);
...
}
Here is FormSettings just for easy reference in case the link dies:
public class FormSettings
{
public string tokenApiUrl { get; set; }
public string tokenApiKey { get; set; }
}
I hope I didn't miss any details, so far it didn't give me any errors but since we do not have unit testing in the project, I won't be able to test immediately. Please let me know if there is something missing with the answer.