如何将正确的混凝土 class 注入封闭系统
How to inject the correct concrete class into a closed sytstem
Hangfire
看起来很漂亮。但是,我在 Hangfire
激活正确的混凝土 class.
时遇到了挑战
一些背景:
public interface IJob
{
bool Execute(string payload);
}
public interface IJobPayload
{
string UserId { get; set; }
string JobName { get; set; }
string JobQueueName { get; set; }
int Id { get; set; }
JobType JobType { get; set; }
CronExpression Cron { get; set; }
}
现在,我有(可能)数百个 所有 继承自 IJob
的作业,并在继承自 IJobPayload
的负载上执行。在不深入了解作业的每个执行代码的情况下,我有类似的东西:
[Queue("critical")] class Job1 : IJob {...}
[Queue("doors")] class Job2 : IJob {...}
[Queue("doors")] class Job3 : IJob {...}
[Queue("lights")] class Job4 : IJob {...}
[Queue("lights")] class Job5 : IJob {...}
[Queue("adhoc")] class Job6 : IJob {...}
...
[Queue("critical")] class JobN : IJob {...}
提供基本作业示例:
public class JobDoorStatusChanged : IJob
{
[Queue("doors")]
public bool Execute(string payload)
{
var command = JsonConvert.DeserializeObject<Payload>(payload);
// handle execution here...
return true/false;
}
public class Payload : IJobPayload
{
public string UserId { get; set; }
public string JobName { get; set; }
public string JobQueueName { get; set; }
public int Id { get; set; }
public JobType JobType { get; set; }
public CronExpression Cron { get; set; }
}
}
我有一个非常简单的 Web API Post 控制器:
[HttpPost]
public string Post()
{
var payload = Request.Content.ReadAsStringAsync().Result;
var queueHandler = new QueueHandler();
return queueHandler.Load(payload);
}
下一步就是我遇到失败的地方。他和我正在成为最好的朋友。不幸的是!
Hangfire 有 4 个入队方法(2 个同步,2 个异步):
public static string Enqueue([NotNull, InstantHandle] Expression<Action> methodCall)
public static string Enqueue([NotNull, InstantHandle] Expression<Func<Task>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Action<T>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)
他们要么采取静态 class:
var id = BackgroundJob.Enqueue(() => MyStaticJob.Execute(payload));
或者他们采用应该可以从 Unity 解析的类型:
var id = BackgroundJob.Enqueue<ConcreteJobDefinition>(a => a.Execute(payload));
因为我有几十个,得分甚至太多 都是基于 IJob 这些入口点都不适合我。
我什至尝试了一个 class 来包装我所有的作业并让 Hangfire 执行单个 class 但到目前为止,运气不好:
public interface IJobService
{
bool Execute(string payload);
}
public class JobService : IJobService
{
private readonly IUnityContainer _container = UnityConfig.GetConfiguredContainer();
public bool Execute(string payload)
{
var command = JsonConvert.DeserializeObject<PayloadStub>(payload);
var job = _container.Resolve<IJob>(command.JobName);
return job.Execute(payload);
}
internal class PayloadStub : IJobPayload
{
public string UserId { get; set; }
public string JobName { get; set; }
public string JobQueueName { get; set; }
public int Id { get; set; }
public JobType JobType { get; set; }
public CronExpression Cron { get; set; }
}
}
现在我可以通过这种方式执行单个具体实现(谈论混淆!):
var id = BackgroundJob.Enqueue<JobService>(a => a.Execute(payload));
仍然没有! 和,你失去了每一份工作的Queue Attribute
!
所以,我回到我的 startup.cs
文件:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
Framework.Initialize(); // internal framework here at work...
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
UnityConfig.RegisterUnity(); // <== UNITY ==
ConfigureAuth(app);
HangfireConfig.RegisterHangfire(app); // <== HANGFIRE ==
}
}
Class UnityConfig:
public class UnityConfig
{
public static void RegisterUnity()
{
var container = Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer();
// Register Auth & Exception handlers...
container.RegisterType<IAccessDeniedResult, AccessDeniedResult>();
container.RegisterType<IExceptionResult, ExceptionResult>();
container.RegisterType<IJobService, JobService>();
// Register all IJob concrete implementations found in this project...
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(type => typeof(IJob).IsAssignableFrom(type) && type.IsClass),
WithMappings.FromAllInterfaces,
t => t.IsNested ? t.DeclaringType.Name + "." + t.Name : t.Name,
WithLifetime.Transient);
}
}
Class HangfireConfig:
public class HangfireConfig
{
public static void RegisterHangfire(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage(Globals.DatabaseHangfire);
GlobalJobFilters.Filters.Add(new LogAttribute());
app.UseHangfireDashboard();
var options = new BackgroundJobServerOptions
{
Queues = Globals.QueueNames,
Activator = new UnityJobActivator(Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer()),
};
app.UseHangfireServer(options);
}
}
我没有发现任何问题。 Unity 拥有所有注册。因为我有这么多基于单一接口的对象,我知道我打破了 Unity 通常用于依赖注入的方式。
有什么想法吗?您可以看到对代码的任何调整吗?
TIA
虽然核心概念略有不同,并且可能有更简洁的方法,但您可以尝试将作业注册为 IEnumerable,然后根据名称选择您需要的作业。我有一个相关的问题,我想根据传入的标准为实现处理 N 个处理程序。我写了一篇关于它的文章 in my blog。
this Stack Overflow answer
中有Unity示例
开发者似乎发现了错误并已修复。我现在正在测试它,到目前为止它看起来比没有更容易。
我还发现我的定义有误。我需要将 Execute 从 bool 更改为 void return 类型。
我有一个类似的问题,并使用一个名为 JobHandler<T>
的通用包装器解决了这个问题,该包装器将作业排入队列。它是这样工作的:
首先是一个碱基JobHandler
:
public abstract class JobHandler
{
public abstract void Enqueue(string payload);
}
接下来,JobHandler<T>
将使用 Hangfire 作为正确的具体类型处理排队:
public abstract class JobHandler<T> : JobHandler
where T: IJob
{
public override void Enqueue(string payload)
{
BackgroundJob.Enqueue<T>(x => x.Execute(payload));
}
}
JobHandler<T>
不一定需要是抽象的,但在我的例子中,我有处理程序执行的额外逻辑,并为每种作业类型提供了它的具体实现。然后将每种类型的处理程序注册到我的应用程序中,我可以简单地执行以下操作:
JobHandler handler = Activator.CreateInstance(GetHandler("MyJobType")) as JobHandler;
handler.Enqueue(payload)
使用 Unity 解决 JobHandler
应该很容易。也就是说,我不确定这是否比简单地解析 IJob
和排队实例有多大好处,因为导致您的问题的错误已得到修复。
Hangfire
看起来很漂亮。但是,我在 Hangfire
激活正确的混凝土 class.
一些背景:
public interface IJob
{
bool Execute(string payload);
}
public interface IJobPayload
{
string UserId { get; set; }
string JobName { get; set; }
string JobQueueName { get; set; }
int Id { get; set; }
JobType JobType { get; set; }
CronExpression Cron { get; set; }
}
现在,我有(可能)数百个 所有 继承自 IJob
的作业,并在继承自 IJobPayload
的负载上执行。在不深入了解作业的每个执行代码的情况下,我有类似的东西:
[Queue("critical")] class Job1 : IJob {...}
[Queue("doors")] class Job2 : IJob {...}
[Queue("doors")] class Job3 : IJob {...}
[Queue("lights")] class Job4 : IJob {...}
[Queue("lights")] class Job5 : IJob {...}
[Queue("adhoc")] class Job6 : IJob {...}
...
[Queue("critical")] class JobN : IJob {...}
提供基本作业示例:
public class JobDoorStatusChanged : IJob
{
[Queue("doors")]
public bool Execute(string payload)
{
var command = JsonConvert.DeserializeObject<Payload>(payload);
// handle execution here...
return true/false;
}
public class Payload : IJobPayload
{
public string UserId { get; set; }
public string JobName { get; set; }
public string JobQueueName { get; set; }
public int Id { get; set; }
public JobType JobType { get; set; }
public CronExpression Cron { get; set; }
}
}
我有一个非常简单的 Web API Post 控制器:
[HttpPost]
public string Post()
{
var payload = Request.Content.ReadAsStringAsync().Result;
var queueHandler = new QueueHandler();
return queueHandler.Load(payload);
}
下一步就是我遇到失败的地方。他和我正在成为最好的朋友。不幸的是!
Hangfire 有 4 个入队方法(2 个同步,2 个异步):
public static string Enqueue([NotNull, InstantHandle] Expression<Action> methodCall)
public static string Enqueue([NotNull, InstantHandle] Expression<Func<Task>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Action<T>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)
他们要么采取静态 class:
var id = BackgroundJob.Enqueue(() => MyStaticJob.Execute(payload));
或者他们采用应该可以从 Unity 解析的类型:
var id = BackgroundJob.Enqueue<ConcreteJobDefinition>(a => a.Execute(payload));
因为我有几十个,得分甚至太多 都是基于 IJob 这些入口点都不适合我。
我什至尝试了一个 class 来包装我所有的作业并让 Hangfire 执行单个 class 但到目前为止,运气不好:
public interface IJobService
{
bool Execute(string payload);
}
public class JobService : IJobService
{
private readonly IUnityContainer _container = UnityConfig.GetConfiguredContainer();
public bool Execute(string payload)
{
var command = JsonConvert.DeserializeObject<PayloadStub>(payload);
var job = _container.Resolve<IJob>(command.JobName);
return job.Execute(payload);
}
internal class PayloadStub : IJobPayload
{
public string UserId { get; set; }
public string JobName { get; set; }
public string JobQueueName { get; set; }
public int Id { get; set; }
public JobType JobType { get; set; }
public CronExpression Cron { get; set; }
}
}
现在我可以通过这种方式执行单个具体实现(谈论混淆!):
var id = BackgroundJob.Enqueue<JobService>(a => a.Execute(payload));
仍然没有! 和,你失去了每一份工作的Queue Attribute
!
所以,我回到我的 startup.cs
文件:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
Framework.Initialize(); // internal framework here at work...
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
UnityConfig.RegisterUnity(); // <== UNITY ==
ConfigureAuth(app);
HangfireConfig.RegisterHangfire(app); // <== HANGFIRE ==
}
}
Class UnityConfig:
public class UnityConfig
{
public static void RegisterUnity()
{
var container = Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer();
// Register Auth & Exception handlers...
container.RegisterType<IAccessDeniedResult, AccessDeniedResult>();
container.RegisterType<IExceptionResult, ExceptionResult>();
container.RegisterType<IJobService, JobService>();
// Register all IJob concrete implementations found in this project...
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(type => typeof(IJob).IsAssignableFrom(type) && type.IsClass),
WithMappings.FromAllInterfaces,
t => t.IsNested ? t.DeclaringType.Name + "." + t.Name : t.Name,
WithLifetime.Transient);
}
}
Class HangfireConfig:
public class HangfireConfig
{
public static void RegisterHangfire(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage(Globals.DatabaseHangfire);
GlobalJobFilters.Filters.Add(new LogAttribute());
app.UseHangfireDashboard();
var options = new BackgroundJobServerOptions
{
Queues = Globals.QueueNames,
Activator = new UnityJobActivator(Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer()),
};
app.UseHangfireServer(options);
}
}
我没有发现任何问题。 Unity 拥有所有注册。因为我有这么多基于单一接口的对象,我知道我打破了 Unity 通常用于依赖注入的方式。
有什么想法吗?您可以看到对代码的任何调整吗?
TIA
虽然核心概念略有不同,并且可能有更简洁的方法,但您可以尝试将作业注册为 IEnumerable,然后根据名称选择您需要的作业。我有一个相关的问题,我想根据传入的标准为实现处理 N 个处理程序。我写了一篇关于它的文章 in my blog。
this Stack Overflow answer
中有Unity示例开发者似乎发现了错误并已修复。我现在正在测试它,到目前为止它看起来比没有更容易。
我还发现我的定义有误。我需要将 Execute 从 bool 更改为 void return 类型。
我有一个类似的问题,并使用一个名为 JobHandler<T>
的通用包装器解决了这个问题,该包装器将作业排入队列。它是这样工作的:
首先是一个碱基JobHandler
:
public abstract class JobHandler
{
public abstract void Enqueue(string payload);
}
接下来,JobHandler<T>
将使用 Hangfire 作为正确的具体类型处理排队:
public abstract class JobHandler<T> : JobHandler
where T: IJob
{
public override void Enqueue(string payload)
{
BackgroundJob.Enqueue<T>(x => x.Execute(payload));
}
}
JobHandler<T>
不一定需要是抽象的,但在我的例子中,我有处理程序执行的额外逻辑,并为每种作业类型提供了它的具体实现。然后将每种类型的处理程序注册到我的应用程序中,我可以简单地执行以下操作:
JobHandler handler = Activator.CreateInstance(GetHandler("MyJobType")) as JobHandler;
handler.Enqueue(payload)
使用 Unity 解决 JobHandler
应该很容易。也就是说,我不确定这是否比简单地解析 IJob
和排队实例有多大好处,因为导致您的问题的错误已得到修复。