I'm trying to use Hangfire Queues in my AspNet Zero project.
I configured the backend to only process specific queues as illustrated in this example:
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfireServer(options =>
{
options.Queues = new[] { "alpha", "beta", "default" };
});
}
However, applying the QueueAttribute to the Execute() method, does not seem to specify which queue the job should be processed on. The entry in the Hangfire database is always default.
public class TestJob : BackgroundJob<int>, ITransientDependency
{
[Queue("alpha")]
public override void Execute(int number)
{
// Do something
}
}
The job is enqueued according to the ABP documentation:
public class MyService
{
private readonly IBackgroundJobManager _backgroundJobManager;
public MyService(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public void Test()
{
_backgroundJobManager.Enqueue<TestJob, int>(42);
}
}
How would one specify the job queue when enqueuing it?
One cannot specify the queue name using ABP as the framework will use dependency injection to resolve the type IBackgroundJob<TArgs> int the HangfireBackgroundJobManager.
When using DI, the interface must be decorated with the [Queue("your_queue"] attribute, and we don't have access to the IBackgroundJob interface at this level for obvious reasons.
I solved this by using Hangfire directly. The code:
public interface ITestJob : ITransientDependency
{
[Queue("alpha")]
public void Execute(int number);
}
public class TestJob : ITestJob, ITransientDependency
{
public void Execute(int number)
{
// Do something
}
}
using Hangfire;
public class MyService
{
public MyService()
{
}
public void Test()
{
BackgroundJob.Enqueue<ITestJob>(x => x.Execute(42));
}
}
Related
I am beginner in autofac and I have to use it in new legacy project asp.net web api.
I am registering of interface and injection works fine with constructor injection.
However, the constructor is being called in numerous places directly new(), and I don't want to replace it everywhere.
So I thought about property injection, but cannot get it to work, the dependency is always null.
The app is split into multiple projects and multiple autofac modules. Autofac configuration as per docs: https://docs.autofac.org/en/latest/integration/webapi.html
I tried to make small demo app, and I was able to get property injection working using all methods from docs: https://autofac.readthedocs.io/en/latest/register/prop-method-injection.html
using Autofac;
public class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().OnActivated(e => e.Instance.MyDependency1 = e.Context.Resolve<IMyDependency>());
//builder.Register(c => new MyService { MyDependency1 = c.Resolve<IMyDependency>() });
//builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
container.Resolve<MyService>();
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}
Unfortunately none of these works for my full project, the object is always null. I know it would be difficult to get help, but maybe someone can advice what to look for?
I just tried reproducing this using the WithProperty registration you have there and the test passes - I can't reproduce it, property injection is working.
If it's not working in your full project, something else is going on. Below is the totally working test I used to verify.
public class ExampleTests
{
[Fact]
public void PropertyInjection()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
var svc = container.Resolve<MyService>();
Assert.NotNull(svc.MyDependency1);
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}
We are getting this exception when calling a web api controller:
InvalidOperationException: Unable to resolve service for type 'SDS.Lambda.Interfaces.ISecretManager' while attempting to activate 'SDS.Lambda.Controllers.SapController'.\r\n <p class="location">Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
StartUp.cs contains the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISecretManager, SecretManager>();
}
The controller has this constructor:
public class SapController : Controller
{
public SapController(ISecretManager secretManager)
{
_secretManager = secretManager;
}
}
We have the same issue with other types being injected into the constructor, but the IConfiguration instance can be injected, for example that parameter does not cause an exception:
public SapController(IConfiguration configuration, ISecretManager secretManager)
The ISecretManager interface looks like this (yes, it really does):
namespace SDS.Lambda.Interfaces
{
public interface ISecretManager
{
}
}
And the class (yes, really - I reduced it down to avoid complexity):
namespace SDS.Lambda.Interfaces
{
public class SecretManager : ISecretManager
{
}
}
Are we providing the interface/concrete type incorrectly?
Is there a way to retrieve the concrete type to test whether it has been provided properly?
When execution reaches the bottom of ConfigureServices, if we look at the services instance result enumeration, in the debugger view, the types we are injecting are listed, so we can't see why they are failing to be instantiated.
UPDATE
To elaborate and explain the issue with another class/dependency in the same solution:
Controller:
namespace SDS.Lambda.Controllers
{
[Route("api/[controller]")]
public class SapController : Controller
{
readonly IHelper helper;
public SapController(IHelper helpme)
{
helper = helpme;
}
...
}
Interface:
namespace SDS.Lambda.Interfaces
{
public interface IHelper
{
}
}
Class:
namespace SDS.Lambda.Helpers
{
public class Helper : IHelper
{
public Helper()
{
}
}
}
StartUp:
namespace SDS.Lambda
{
public class Startup
{
public static IConfiguration Configuration { get; private set; }
private readonly AppSettings _appSettings;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
_appSettings = configuration.GetSection("AppSettings").Get<AppSettings>();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(logger => logger.AddLambdaLogger());
services.AddSingleton<IHelper, Helper>();
services.AddControllers();
}
...
}
I'm having some difficulties getting a custom action filter to work in ASP.NET Core 3.1 Web API. I've followed this SO, as well as the Microsoft docs, but it's not working. I've created a simple filter (note: I need Dependency Injection);
public class LogFilterAttribute : ActionFilterAttribute, IFilterMetadata
{
private readonly ILogger<LogFilterAttribute> _logger;
public LogFilterAttribute(ILogger<LogFilterAttribute> logger)
{
_logger = logger;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
_logger.LogWarning("test");
base.OnActionExecuting(actionContext);
}
}
Notes:
ActionFilterAttribute is from System.Web.Http.Filters namespace.
I implemented IFilterMetadata (which is just a marker interface) as this seems to be required by ServiceFilter and TypeFilter.
I'm registering this in ConfigureServices of Startup.cs as follows:
services.AddScoped<LogFilterAttribute>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
and then applying this in my Web API controller as follows:
[ApiVersion("1.0")]
[ApiController]
[Route("v{version:apiVersion}/resources/{id}")]
public class ResourceController : ControllerBase
{
private readonly ILogger<ResourceController> _logger;
public ResourceController(ILogger<ResourceController> logger)
{
_logger = logger;
}
[HttpGet]
[ServiceFilter(typeof(LogFilterAttribute))]
public async Task<IActionResult> Get([FromRoute(Name = "id")] string id)
{
_logger.LogInformation($"{typeof(ResourceController)}.{nameof(Get)}");
return Ok();
}
}
I've tried with both ServiceFilter and TypeFilter, but to no avail - it just skips the break-point in filter and goes straight to my route logic. What am I doing wrong?
Try implementing IActionFilter in place of ActionFilterAttribute
In the end I solved the issue by implementing IAsyncActionFilter and inheriting from Attribute as follows:
public class LogFilterActionFilterAttribute : Attribute, IAsyncActionFilter
{
public LogFilterActionFilterAttribute(...)
{
...
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
...
}
}
I also override TypeFilterAttribute as follows:
public class LogFilterAttribute : TypeFilterAttribute
{
public LogFilterAttribute (...) : base(typeof(LogFilterActionFilterAttribute))
{
Arguments = new object[] { ... };
}
}
So that I can decorate on controllers/routes as follows:
[ApiVersion("1.0")]
[ApiController]
[Route("v{version:apiVersion}/resources/{id}")]
public class ResourceController : ControllerBase
{
private readonly ILogger<ResourceController> _logger;
public ResourceController(ILogger<ResourceController> logger)
{
_logger = logger;
}
[HttpGet]
[LogFilter(...)]
public async Task<IActionResult> Get([FromRoute(Name = "id")] string id)
{
_logger.LogInformation($"{typeof(ResourceController)}.{nameof(Get)}");
return Ok();
}
}
In StartUp.cs, Add the filter in MVC pipeline.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(typeof(LogFilterAttribute));
});
}
You need to use this on the controller/method as you're using a type filter, isn't the logger already scoped within the configuration? if so you need a type filter
[TypeFilter(typeof(LogFilterAttribute))]
For my use, I don't need to add IFilterMetadata
I want to change data inside a Vaadin UI. The change is invoked by a a rest call. There, i somehow need a reference to the UI class to call its method´, e.g. changeValue(string value).
I'm using vaadin-spring-boot-starter 1.0.0
Is that somehow possible?
EDIT: Another question now:
I was trying to do that Server Push, mentioned by #Eric, inside of a View, so that the view will get updated on a Broadcast message. However, this is not working (no exceptions, nothing to debug, just no updates in the view). This is what i do in my View:
#UIScope
#SpringView(name = LoadWebsiteView.VIEW_NAME)
#Push
public class LoadWebsiteView extends VerticalLayout implements View, Broadcaster.BroadcastListener {
...
#Autowired
public LoadWebsiteView(ScraperMainUI scraperMainUi) {
this.scraperMainUi = scraperMainUi;
Broadcaster.register(this);
initControlPane();
}
#Override
public void receiveBroadcast(String message) {
scraperMainUi.access(new Runnable() {
#Override
public void run() {
urlTxtField.setValue(message);
}
});
}
and here is the simple stuff i do in my restcontroller:
Broadcaster.broadcast(text);
What you are looking for is Vaadin's Push feature and a way to send a message to a list of registered "clients" (in this case, the Vaadin UIs who need to known about the changes).
You can read about Vaadin Push here: Enabling Server Push and also in the article Advanced Push
The Vaadin push function allows your server to force updates to the client instead of waiting on the browser to request again.
The message component simply acts as a way to tell subscribed UIs that there is an update they need to action.
This said, I have a project that does about the same as multiple users are actioning items and there are Spring scheduled tasks that also can effect changes the user needs to know about.
Note, the below examples are based on the examples available in Enabling Server Push article.
Broadcaster.java - Acts as the mechanism that registers instances to receive broadcasts and provides a facility to send broadcasts. In the below example, I have I have a class that represents a message (BroadcastMessage) but you could simplify it of course.
public class Broadcaster implements Serializable {
private static final long serialVersionUID = 3540459607283346649L;
static ExecutorService executorService = Executors.newSingleThreadExecutor();
private static LinkedList<BroadcastListener> listeners = new LinkedList<BroadcastListener>();
public interface BroadcastListener {
void receiveBroadcast(BroadcastMessage message);
}
public static synchronized void register(BroadcastListener listener) {
listeners.add(listener);
}
public static synchronized void unregister(BroadcastListener listener) {
listeners.remove(listener);
}
public static synchronized void broadcast(final BroadcastMessage message) {
for (final BroadcastListener listener: listeners)
executorService.execute(new Runnable() {
#Override
public void run() {
listener.receiveBroadcast(message);
}
});
}
}
Here is the class I defined for my BroadcastMessage. The idea is to have a way to denote what kind of message I have and also some payload in the form of a Map
public class BroadcastMessage implements Serializable {
private static final long serialVersionUID = 5637577096751222106L;
public BroadcastMessageType messageType;
public Map<String, String> params;
public BroadcastMessage() {
}
public BroadcastMessage(BroadcastMessageType messageType) {
this.messageType = messageType;
this.params = new HashMap<String, String>();
}
public BroadcastMessage(BroadcastMessageType messageType, Map<String, String> params) {
this.messageType = messageType;
this.params = params;
}
public BroadcastMessageType getMessageType() {
return messageType;
}
public void setMessageType(BroadcastMessageType messageType) {
this.messageType = messageType;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
}
This is an example Vaadin UI that wants to listen for Broadcasts. Note the #Push annotation. Without this, the client will only refresh when the browser decides to. #Push makes it immediate**
#SpringComponent
#UIScope
#Push
#SpringView(name=TaskListComponent.NAME)
public class TaskListComponent extends MyCustomComponent implements Broadcaster.BroadcastListener, View {
/** PRUNED DOWN, TO DEMONSTRATE THE KEY CODE **/
// Register this window when we enter it
#Override
public void enter(ViewChangeEvent event) {
Broadcaster.register(this);
}
// Must also unregister when the UI expires
#Override
public void detach() {
Broadcaster.unregister(this);
super.detach();
}
// Receive a broadcast
#Override
public void receiveBroadcast(BroadcastMessage message) {
getUI().access(new Runnable() {
#Override
public void run() {
// DO WHATEVER YOU NEED TO DO HERE.
// I CALLED INITIALIZE BUT IT COULD BE
// JUST YOU FIELD UPDATE
if ( message.getMessageType().equals(BroadcastMessageType.REFRESH_TASK_LIST) )
initialize();
}
});
}
}
To send a message from your rest interface:
Broadcaster.broadcast(
new BroadcastMessage(
BroadcastMessageType.AUTO_REFRESH_LIST
)
);
Hope this helps! :)
Here is my ConfigUpdater class
private final class ConfigUpdater implements ManagedService {
#SuppressWarnings("rawtypes")
#Override
public void updated(Dictionary config) throws ConfigurationException {
if (config == null) {
return;
}
String title = ((String)config.get("title"));
}
}
My question is how can I access String title in any other class? Or how can I get config dictionary in any other class... Method updated will only be called when a config file is changed... once it is changed how can access its data in other class?
In general you would create a service that exposes these properties to other components.
For example, you could give your ConfigUpdater a second interface. Another component can than lookup/inject this interface from the service registry and use it's methods to access the properties.
I created an example project on GitHub: https://github.com/paulbakker/configuration-example
The most important part is the service that implements both ManagedService and a custom interface:
#Component(properties=#Property(name=Constants.SERVICE_PID, value="example.configurationservice"))
public class ConfigurationUpdater implements ManagedService, MyConfiguration{
private volatile String message;
#Override
public void updated(#SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
message = (String)properties.get("message");
}
#Override
public String getMessage() {
return message;
}
}
The configuration can then be used like this:
#Component(provides=ExampleConsumer.class,
properties= {
#Property(name = CommandProcessor.COMMAND_SCOPE, value = "example"),
#Property(name = CommandProcessor.COMMAND_FUNCTION, values = {"showMessage"}) })
public class ExampleConsumer {
#ServiceDependency
private volatile MyConfiguration config;
public void showMessage() {
String message = config.getMessage();
System.out.println(message);
}
}