WebJob 依赖注入绑定到泛型 Class
WebJob Dependency Injection Binding to Generic Class
我有一系列 类 用于处理看起来像这样的 WebJobs:
public class EnvelopeSalutationJob : BatchJob
{
public EnvelopeSalutationJob( StringWriter swLogger )
: base( swLogger, "Envelope Salutation Job" )
{
}
[ Singleton() ]
public async Task ProcessMessage(
[ QueueTrigger( "%" + nameof( ContainerQueueConstants.EnvelopeSalutation ) + "%" ) ] EnvelopeSalutationMessage msg,
TextWriter azureLogWriter
)
{
PhaseNames.SetNames( "Processing Homes", "Job Completed" );
await ExecuteFromMessage( msg, azureLogWriter, Launch );
}
}
这些使用 AutoFac 作为 DI 框架时效果很好,配置如下所示:
public static class ContainerConfig
{
public static IContainer GetContainer()
{
var builder = new ContainerBuilder();
// per job
builder.RegisterType<StringWriter>();
// jobs
builder.RegisterType<EnvelopeSalutationJob>();
return builder.Build();
}
}
但最近我想 "genericize" 作业,这样它们就不会绑定到特定的 DbContext。我尝试使用这样的模式:
public class EnvelopeSalutationJob<TContext, TUser>
: BatchJob<TContext, TUser>
where TContext : IdentityDbContext<TUser>, ICampaignContext, new()
where TUser : IdentityUser, INamedUser
{
public EnvelopeSalutationJob( StringWriter swLogger )
: base( swLogger, "Envelope Salutation Job" )
{
}
[ Singleton() ]
public async Task ProcessMessage(
[ QueueTrigger( "%" + nameof( ContainerQueueConstants.EnvelopeSalutation ) + "%" ) ] EnvelopeSalutationMessage msg,
TextWriter azureLogWriter
)
{
PhaseNames.SetNames( "Processing Homes", "Job Completed" );
await ExecuteFromMessage( msg, azureLogWriter, Launch );
}
}
AutoFac 配置更改为:
public static class ContainerConfig
{
public static IContainer GetContainer()
{
var builder = new ContainerBuilder();
// per job
builder.RegisterType<StringWriter>();
// jobs
builder.RegisterType<EnvelopeSalutationJob<ConnellJobContext, ConnellUser>>();
return builder.Build();
}
}
不幸的是,这不起作用。 WebJobs 控制台应用程序启动正常,但它抱怨:
No job functions found. Try making your job classes and methods
public.
是否无法将作业绑定到通用实例 类?
根据你的描述,我已经在我这边测试了这个问题,我可以重现这个问题,如下所示:
我勾选Azure WebJobs SDK source code on git, I found that DefaultTypeLocator.cs would use IsJobClass
that invoking Type.ContainsGenericParameters过滤类型后如下:
public static bool IsJobClass(Type type)
{
if (type == null)
{
return false;
}
return type.IsClass
// For C# static keyword classes, IsAbstract and IsSealed both return true. Include C# static keyword
// classes but not C# abstract keyword classes.
&& (!type.IsAbstract || type.IsSealed)
// We only consider public top-level classes as job classes. IsPublic returns false for nested classes,
// regardless of visibility modifiers.
&& type.IsPublic
&& !type.ContainsGenericParameters;
}
此外,FunctionIndexer.cs 将使用 IsJobMethod
过滤方法如下:
public bool IsJobMethod(MethodInfo method)
{
if (method.ContainsGenericParameters)
{
return false;
}
if (method.GetCustomAttributesData().Any(HasJobAttribute))
{
return true;
}
if (method.GetParameters().Length == 0)
{
return false;
}
if (method.GetParameters().Any(p => p.GetCustomAttributesData().Any(HasJobAttribute)))
{
return true;
}
return false;
}
据我了解,Azure WebJobs SDK 目前不支持将作业绑定到通用 类 的实例。您可以定义 BaseDbContext
并利用构造函数依赖注入来初始化 DbContext
实例。另外,您可以添加您的反馈 here.
我有一系列 类 用于处理看起来像这样的 WebJobs:
public class EnvelopeSalutationJob : BatchJob
{
public EnvelopeSalutationJob( StringWriter swLogger )
: base( swLogger, "Envelope Salutation Job" )
{
}
[ Singleton() ]
public async Task ProcessMessage(
[ QueueTrigger( "%" + nameof( ContainerQueueConstants.EnvelopeSalutation ) + "%" ) ] EnvelopeSalutationMessage msg,
TextWriter azureLogWriter
)
{
PhaseNames.SetNames( "Processing Homes", "Job Completed" );
await ExecuteFromMessage( msg, azureLogWriter, Launch );
}
}
这些使用 AutoFac 作为 DI 框架时效果很好,配置如下所示:
public static class ContainerConfig
{
public static IContainer GetContainer()
{
var builder = new ContainerBuilder();
// per job
builder.RegisterType<StringWriter>();
// jobs
builder.RegisterType<EnvelopeSalutationJob>();
return builder.Build();
}
}
但最近我想 "genericize" 作业,这样它们就不会绑定到特定的 DbContext。我尝试使用这样的模式:
public class EnvelopeSalutationJob<TContext, TUser>
: BatchJob<TContext, TUser>
where TContext : IdentityDbContext<TUser>, ICampaignContext, new()
where TUser : IdentityUser, INamedUser
{
public EnvelopeSalutationJob( StringWriter swLogger )
: base( swLogger, "Envelope Salutation Job" )
{
}
[ Singleton() ]
public async Task ProcessMessage(
[ QueueTrigger( "%" + nameof( ContainerQueueConstants.EnvelopeSalutation ) + "%" ) ] EnvelopeSalutationMessage msg,
TextWriter azureLogWriter
)
{
PhaseNames.SetNames( "Processing Homes", "Job Completed" );
await ExecuteFromMessage( msg, azureLogWriter, Launch );
}
}
AutoFac 配置更改为:
public static class ContainerConfig
{
public static IContainer GetContainer()
{
var builder = new ContainerBuilder();
// per job
builder.RegisterType<StringWriter>();
// jobs
builder.RegisterType<EnvelopeSalutationJob<ConnellJobContext, ConnellUser>>();
return builder.Build();
}
}
不幸的是,这不起作用。 WebJobs 控制台应用程序启动正常,但它抱怨:
No job functions found. Try making your job classes and methods public.
是否无法将作业绑定到通用实例 类?
根据你的描述,我已经在我这边测试了这个问题,我可以重现这个问题,如下所示:
我勾选Azure WebJobs SDK source code on git, I found that DefaultTypeLocator.cs would use IsJobClass
that invoking Type.ContainsGenericParameters过滤类型后如下:
public static bool IsJobClass(Type type)
{
if (type == null)
{
return false;
}
return type.IsClass
// For C# static keyword classes, IsAbstract and IsSealed both return true. Include C# static keyword
// classes but not C# abstract keyword classes.
&& (!type.IsAbstract || type.IsSealed)
// We only consider public top-level classes as job classes. IsPublic returns false for nested classes,
// regardless of visibility modifiers.
&& type.IsPublic
&& !type.ContainsGenericParameters;
}
此外,FunctionIndexer.cs 将使用 IsJobMethod
过滤方法如下:
public bool IsJobMethod(MethodInfo method)
{
if (method.ContainsGenericParameters)
{
return false;
}
if (method.GetCustomAttributesData().Any(HasJobAttribute))
{
return true;
}
if (method.GetParameters().Length == 0)
{
return false;
}
if (method.GetParameters().Any(p => p.GetCustomAttributesData().Any(HasJobAttribute)))
{
return true;
}
return false;
}
据我了解,Azure WebJobs SDK 目前不支持将作业绑定到通用 类 的实例。您可以定义 BaseDbContext
并利用构造函数依赖注入来初始化 DbContext
实例。另外,您可以添加您的反馈 here.