I'm currently trying to use a repository to update some data in the DB using quartz.net.
Keep in mind that I'm using ASP.Net Core 3.1
The problem that I'm currently having is that when I'm injecting my IUserProjectRepository in the constructor of the IJob the job wont get executed and I also get an error in the Quartz DB implementation:
So, this is how my startup.cs looks like:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<UserProjectStatusJob>();
services.AddTransient(provider => GetScheduler().Result);
}
....
private async Task<IScheduler> GetScheduler()
{
NameValueCollection properties = new NameValueCollection
{
{ "quartz.scheduler.instanceName", "Cliche" },
{ "quartz.scheduler.instanceId", "Cliche" },
{ "quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" },
{ "quartz.jobStore.useProperties", "true" },
{ "quartz.jobStore.dataSource", "default" },
{ "quartz.jobStore.tablePrefix", "QRTZ_" },
{
"quartz.dataSource.default.connectionString",
"connectionstring"
},
{ "quartz.dataSource.default.provider", "SqlServer" },
{ "quartz.threadPool.threadCount", "1" },
{ "quartz.serializer.type", "json" },
};
var schedulerFactory = new StdSchedulerFactory(properties);
var scheduler = await schedulerFactory.GetScheduler();
await scheduler.Start();
return scheduler;
}
This is how my Job (UserProjectStatusJob) Looks like:
public class UserProjectStatusJob : IJob
{
private IUserProjectRepository _userProjectRepository;
public UserProjectStatusJob(IUserProjectRepository userProjectRepository)
{
this._userProjectRepository = userProjectRepository;
}
public Task Execute(IJobExecutionContext context)
{
try
{
JobDataMap dataMap = context.JobDetail.JobDataMap;
string userProjectId = dataMap.GetString("userProjectId");
string userProjectProjectId = dataMap.GetString("userProjectProjectId");
_userProjectRepository.CloseUserProject(userProjectProjectId, userProjectId);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return Task.FromResult(0);
}
}
I create my job in the same UserProjectRepository:
public class UserProjectRepository : IUserProjectRepository
{
private readonly ApplicationDbContext _dbContext;
private readonly IFileService _fileService;
private readonly INotificationRepository _notificationRepository;
private readonly IScheduler _scheduler;
public UserProjectRepository(ApplicationDbContext dbContext,
IFileService fileService,
INotificationRepository notificationRepository,
IScheduler scheduler)
{
this._scheduler = scheduler;
this._notificationRepository = notificationRepository;
this._fileService = fileService;
this._dbContext = dbContext;
}
public async Task CreateCronJobForUserProject(UserProject userProject)
{
// Add Later in to startAt
TimeSpan timeToTrigger = userProject.Project.Assignment.DeadLine - DateTime.Now;
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity($"Check Availability-{DateTime.Now}")
.StartAt(DateTime.Now.AddSeconds(15))
.WithPriority(1)
.Build();
IDictionary<string, object> map = new Dictionary<string, object>()
{
{"userProjectId", $"{userProject.Id}" },
{"userProjectProjectId", $"{userProject.ProjectId}" },
};
IJobDetail job = JobBuilder.Create<UserProjectStatusJob>()
.WithIdentity($"Check Availability-{DateTime.Now}")
.SetJobData(new JobDataMap(map))
.Build();
await this._scheduler.ScheduleJob(job, trigger);
}
}
EDIT:
Error:
After taking a closer look I did found this:
[14:46:50 ERR] An error occurred instantiating job to be executed. job= 'DEFAULT.Check Availability-10/28/2020 14:46:35'
Quartz.SchedulerException: Problem instantiating class 'IKL.Data.Services.UserProjectStatusJob: Cannot instantiate type which has no empty constructor (Parameter 'UserProjectStatusJob')'
---> System.ArgumentException: Cannot instantiate type which has no empty constructor (Parameter 'UserProjectStatusJob')
Related
I have written a middleware for my web application.
The middleware handles special requests and writes multiple information from multiple sources to the log. For example like the following code snippet:
public class MyMiddleware
{
public async Task Invoke(HttpContext context)
{
var dataToBeLogged = await context.Request.ReadFromJsonAsync<LogEntry[]>();
foreach(var l in dataToBeLogged)
{
var loggerName = string.IsNullOrEmpty(l.LoggerName) ? "Default" : l.LoggerName;
var logger = _loggerFactory.CreateLogger($"{env.ApplicationName}.Client.{loggerName}");
logger.Log(l.Level, l.Exception, l.Message);
}
}
}
The loggerName and therefor the logger might be differ but it could also be that a logger with the same loggerName hast been created before.
My question is what is the best practice of handling the logger creation?
Should I always create a new logger? or
Should I create a Dictionary where I store loggers which has been created before in the Invoke method (Example 1)? or
Because the instance of the middleware doesn't change at runtime, should I create the dictionary a class level (Example 2)?
Example 1
public class MyMiddleware
{
public async Task Invoke(HttpContext context)
{
var dataToBeLogged = await context.Request.ReadFromJsonAsync<LogEntry[]>();
var dict = new Dictionary<string, ILogger>();
foreach(var l in dataToBeLogged)
{
var loggerName = string.IsNullOrEmpty(l.LoggerName) ? "Default" : l.LoggerName;
if (!dict.ContainsKey(loggerName))
dict.Add(loggerName, _loggerFactory.CreateLogger($"{env.ApplicationName}.Client.{loggerName}"));
var logger = dict[loggerName];
logger.Log(l.Level, l.Exception, l.Message);
}
}
}
Example 2
public class MyMiddleware
{
private readonly dict = new Dictionary<string, ILogger>();
public async Task Invoke(HttpContext context)
{
var dataToBeLogged = await context.Request.ReadFromJsonAsync<LogEntry[]>();
foreach(var l in dataToBeLogged)
{
var loggerName = string.IsNullOrEmpty(l.LoggerName) ? "Default" : l.LoggerName;
if (!dict.ContainsKey(loggerName))
dict.Add(loggerName, _loggerFactory.CreateLogger($"{env.ApplicationName}.Client.{loggerName}"));
var logger = dict[loggerName];
logger.Log(l.Level, l.Exception, l.Message);
}
}
}
LogEntry class
public class LogEntry
{
public string LoggerName { get;set; }
public int Level { get;set; }
public Exception Exception { get;set; }
public string Message { get;set; }
}
Example data
[
{ loggerName: "LoggerOne", level: 2, exception: null, message: "This is an information" },
{ loggerName: "LoggerTwo", level: 3, exception: null, message: "This is a warning" },
{ loggerName: "LoggerOne", level: 4, exception: { message: "A Exception message", stackTrace: "..." }, message: "This is an error" }
]
Expected log output
MyProject.Client.LoggerOne: Information: This is an information
MyProject.Client.LoggerTwo: Warning: This is a warning
MyProject.Client.LoggerOne: Error: This is an error
MyProject.Client.LoggerOne: Error: A Exception message
at ...
at ...
at ...
On the front side when the user adds a new mp3. In addition to writing this to the database, I also need to transfer that mp3 to the client side. But my client side is Worker Service. I need to transfer this mp3 to that Worker Service via SignalR.
My SignalR server project codes :
Startup hub endpoint :
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}");
endpoints.MapHub<TrackHub>("/hubs/track");
});
Hub :
public class TrackHub : Hub
{
public static Dictionary<int, string> ActiveUsers = new Dictionary<int, string>();
public TrackHub()
{
}
public async Task SendMessage(string message)
{
var connectionId = Context.ConnectionId;
await Clients.Others.SendAsync("ReceiveMessage", message + connectionId);
}
public override async Task OnConnectedAsync()
{
await Clients.All.SendAsync("ActiveUsers");
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.All.SendAsync("ActiveUsers");
}
}
HubContext
public class TrackBusiness
{
private readonly IHubContext<TrackHub> _hubContext;
public TrackBusiness(IHubContext<TrackHub> hubContext)
{
_hubContext = hubContext;
}
public Task SendMessage()
{
return _hubContext.Clients.All.SendAsync("Receive");
}
}
And my client side - worker service project :
public class SocketService : ISocketService
{
private HubConnection? _connection;
private readonly IConfiguration _configuration;
private readonly ILogger<SocketService> _logger;
public event OnNewTrackAddedEventHandler NewTrackAdded;
public event OnDeviceDeletedEventHandler DeviceDeleted;
public SocketService(IConfiguration configuration, ILogger<SocketService> logger)
{
_configuration = configuration;
_logger = logger;
}
public async Task Start()
{
_connection = new HubConnectionBuilder()
.WithUrl(_configuration.GetValue<string>("SocketUrl"))
.WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) })
.Build();
_connection.Reconnected += connectionId =>
{
if (_connection.State == HubConnectionState.Connected)
{
_logger.LogInformation("Socket reconnected");
}
if (_connection.State == HubConnectionState.Reconnecting)
{
_logger.LogInformation("Socket try to reconnect");
}
return Task.CompletedTask;
};
_connection.On<string, byte[]>("SendMessage", (imei, track) =>
{
NewTrackAdded.Invoke(imei, track);
});
_connection.On<string>("DeviceDeleted", (imei) =>
{
DeviceDeleted.Invoke(imei);
});
try
{
await _connection.StartAsync();
_logger.LogInformation("Socket started");
}
catch (Exception e)
{
_logger.LogWarning("Socket can't connect : {0}", e.Message);
}
}
}
My appSettings :
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ApiUrl": "localhost",
"SocketUrl": "http://localhost:29082/hubs/track"
}
But I run into this error when I start the project. Can you help with the problem?
I run integration tests for my asp.net core application, the call passes from multiple middle-wares but stops at one of them which has the following line :
var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
var attribute = endpoint?.Metadata.GetMetadata<AllowAHeader>();
The endpoint is null.
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override IHostBuilder CreateHostBuilder()
{
var builder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(x =>
{
x.UseStartup<TStartup>().UseTestServer();
});
return builder;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
builder.ConfigureTestServices(services =>
{
services.RemoveAll<DbContext>();
services.RemoveAll<DbContextOptions>();
foreach (var option in services.Where(s =>
s.ServiceType.BaseType ==
typeof(DbContextOptions)).ToList())
{
services.Remove(option);
}
services.AddDbContext<DbContext>(options =>
{
options.UseInMemoryDatabase("Testing");
});
});
}
}
Here is the test
public class ClientTests : IClassFixture<CustomWebApplicationFactory<TestStartup>>
{
private readonly HttpClient _client;
public ClientTests(CustomWebApplicationFactory<TestStartup> customWebApplicationFactory)
{
_client = customWebApplicationFactory.CreateClient();
}
[Fact]
public async Task GetClients()
{
_client.DefaultRequestHeaders.Add("X-Integration-Testing", "True");
_client.DefaultRequestHeaders.Add("X-Integration-Authroize", "Basic");
var result = await _client.PostAsync("v1/client", null);
}
}
The TestStartup :
public class TestStartup : Startup
{
public TestStartup(IConfiguration configuration)
: base(configuration)
{
}
protected override void ConfigureMiddlewareForIntegrationTest(IApplicationBuilder app)
{
app.UseMiddleware<AuthenticatedTestRequestMiddleware>();
}
}
public class AuthenticatedTestRequestMiddleware
{
public const string TestingHeader = "X-Integration-Testing";
public const string TestingHeaderAuthValueValue = "X-Integration-Authroize";
private readonly RequestDelegate _next;
public AuthenticatedTestRequestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.Keys.Contains(TestingHeader))
{
if (context.Request.Headers.Keys.Contains(TestingHeaderAuthValueValue))
{
var encoded = "Basic " + System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("user" + ":" + "123456"));
context.Request.Headers.Add("Authorization", encoded);
}
}
}
}
In ConfigureWebHostDefaults add:
x.UseHttpSys(opt =>
opt.RequestQueueMode = RequestQueueMode.Create;
)
Have not figured out exactly why it's needed, but I'm guessing it's a bug being the value of RequestQueueMode is 0 by default, same as RequestQueueMode.Create's value.
Below is my seed class..
public static class DataInitializer
{
public static async void SeedRolesAsync(RoleManager<Role> roleManager)
{
if (roleManager.RoleExistsAsync("Administrator").Result)
return;
var role = new Role
{
Name = "Administrator",
Description = "Perform all the operations."
};
await roleManager.CreateAsync(role);
}
public static async void SeedRoleClaimsAsync(RoleManager<Role> roleManager)
{
var role = await roleManager.FindByNameAsync("Administrator");
var roleClaims = await roleManager.GetClaimsAsync(role);
foreach (var claimString in AllClaims.GetList())
{
var newClaim = new Claim(claimString, "");
if (!roleClaims.Any(rc => rc.Type.ToString() == claimString))
{
await roleManager.AddClaimAsync(role, newClaim);
}
}
}
public static async void SeedUsersAsync(UserManager<User> userManager)
{
var user = new User
{
UserName = "admin#example.com",
Email = "admin#example.com",
FirstName = "Admin",
LastName = "User",
Enabled = true
};
var result = await userManager.CreateAsync(user, "Admin#123");
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, "Administrator");
}
}
public static void SeedData(UserManager<User> userManager, RoleManager<Role> roleManager)
{
SeedRolesAsync(roleManager);
SeedRoleClaimsAsync(roleManager);
SeedUsersAsync(userManager);
}
}
calling this method in startup class DataInitializer.SeedData(userManager, roleManager);
Am getting error below error while seeding.. i am using ef core 3 for postgresql.. Am getting
System.ObjectDisposedException: 'Cannot access a disposed object.
Object name: 'MyUserManager'.'
Try using tasks all the way - if you don't return anything to wait on chances are the code will continue with disposal even when something hasn't completed yet.
public static async Task SeedData(UserManager<User> userManager, RoleManager<Role> roleManager)
{
await SeedRolesAsync(roleManager);
await SeedRoleClaimsAsync(roleManager);
await SeedUsersAsync(userManager);
}
public static async Task SeedRolesAsync(RoleManager<Role> roleManager)
{
⋮
}
public static async Task SeedRoleClaimsAsync(RoleManager<Role> roleManager)
{
⋮
}
public static async Task SeedUsersAsync(UserManager<User> userManager)
{
⋮
}
You can also move this call into the Main method of your Program.cs because that method can be made async.
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
using (var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>())
using (var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<Role>>())
{
await DataInitializer.SeedData(userManager, roleManager).ConfigureAwait(false);
}
await host.RunAsync().ConfigureAwait(false);
}
I've been using this method in an older version of Swagger:
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (authAttributes.Any())
{
operation.Responses.Add("401", new Response { Description = "Unauthorized" });
operation.Security = new List<IDictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new[] { "myscope" } }
}
};
}
}
}
However, now the interface has changed into this:
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
throw new NotImplementedException();
}
}
How do I convert the code? :-)
Many thanks!
Gunnar
You could refer to the official github doc to change like below:
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo
.GetCustomAttributes(true)
.OfType<AuthorizeAttribute>()
.Select(attr => attr.Policy)
.Distinct();
if (authAttributes.Any())
{
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
var oAuthScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
};
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[ oAuthScheme ] = authAttributes.ToList()
}
};
}
}
}