我有一个使用我的MVC azure web应用程序部署的Azure webjob。这个作业有两个队列触发的功能,一个是获取PDF文件并将其分成单个页面图像,然后是第二个处理各个页面的功能。我在网站和webjob上都使用EF 6和Unity。
使用IJobActivator选项我将DbContext和UoW / Services / Repositories注入webjob没有任何问题。当我尝试并行运行webjob时,问题似乎就出现了。如果我将webjob配置选项设置为config.Queues.BatchSize = 1,一切都很好 - 但速度慢(串行,一次一页)。如果我将BatchSize提升到大于1的任何值,那么我会在&#34的行中出现错误;在先前的异步操作完成之前,在此上下文中启动第二个操作"。由于注入了DbContext,我尝试使用LifetimeManager选项来查看是否存在问题 - 没有变化。错误说我需要确保我的所有EF调用都使用" await" (是异步的),但它们......所以这似乎不是问题。
如果我无法并行运行,那么webjob的用处将会严重减少。有什么想法吗?在webjob中放弃EF和DbContext并使用ADO.Net直接转到数据库会不会更好?
答案 0 :(得分:1)
错误消息很明显,您无法同时对同一个DbContext进行多次未完成的异步操作。由于您肯定希望保持工作并行性(即不要将BatchSize设置为1),因此您可能只需要为每个作业函数调用使用一个新的DbContext。 DbContext是轻量级的,所以这对你来说不是一个问题。
官方DbContext documentation还提供了有关在WebApps中管理DbContext实例的以下指导:"使用Web应用程序时,请按请求使用上下文实例。" 该指南适用于您的WebJob案例。
答案 1 :(得分:0)
正如我的评论中所提到的,并行性问题的解决方案归结为将DbContext生命周期保持在WebJob的单个方法中,因为EF不是线程安全的。尝试使用Unity和IJobActivator将这些东西保存在更高级别(在方法之外),甚至更改LifetimeManager也没有效果。通过摆脱Unity并在DbContext上的WebJob中创建一个“使用”语句,它保证了一切安全和有效。
生产Azure上的SQL数据库丢失/重置似乎与EF DB初始化程序和迁移有关。我将开始另一个问题,因为它与我原来的问题没有直接关系。
答案 2 :(得分:0)
您是如何配置团结注册的? 我想如果你想注入你的DbContext,你应该只使用一个注册来创建一个每次调用的新实例。每次新消息到达队列时,框架将使用IJobActivator获取依赖项。
实现了IJobActivator/// <summary>
/// An activator that uses <see cref="SimpleInjector"/> to return instance of a job type.
/// </summary>
public class SimpleInjectorJobActivator : IJobActivator
{
/// <summary>
/// Gets or sets the container to resolve dependencies.
/// </summary>
private readonly Container _container;
/// <summary>
/// Initialize a new instance of the <see cref="SimpleInjectorJobActivator"/> class.
/// </summary>
/// <param name="container">The <see cref="Container"/> to resolve dependencies.</param>
public SimpleInjectorJobActivator(Container container)
{
_container = container;
}
/// <summary>
/// Creates a new instance of a job type.
/// </summary>
/// <typeparam name="T">The job type.</typeparam>
/// <returns>
/// A new instance of the job type.
/// </returns>
public T CreateInstance<T>()
{
return (T)_container.GetInstance(typeof(T));
}
}
如果您为每个队列触发创建一个作业处理器,您可以创建两个类:
public class MyJobProcessor1
{
private readonly MyDbContext _myDbContext;
public MyJobProcessor1(MyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
public void Run([QueueTrigger("my-queue1")] BrokeredMessage message)
{
}
}
我的主要职能是:
static void Main()
{
// Register my dependency, by default simple injector create a new instance per call
var container = new Container();
container.Register(() => new MyDbContext());
container.Verify();
// Configure the job host
var config = new JobHostConfiguration
{
JobActivator = new SimpleInjectorJobActivator(container)
};
// Run the host
var host = new JobHost(config);
host.RunAndBlock();
}
每次收到新邮件时,IJobActivator都会返回一个在我的IoC容器中注册的DbContext的新实例。