问题描述
我有一个带有所有控制器的网站角度前端和后端WebAPI,我还有一个服务(C#类),我将其作为一个长期运行的任务调用以侦听传入的Azure服务总线消息。
仅供参考-我无法将任何作用域服务(DbContext)传递给单一实例(ServiceBusConsumer),因此无法将我的DB上下文传递给此服务。
问题-收到传入的Service Bus消息后,如何调用数据库并使用它?
这是我侦听和接收消息的服务。
Startup.cs
services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();
Program.cs->;in Main()I启动服务
var bus = services.GetRequiredService<IServiceBusConsumer>();
bus.RegisterOnMessageHandlerAndReceiveMessages();
ServiceBusConsumer er.cs
public class ServiceBusConsumer : IServiceBusConsumer
{
private readonly IConfiguration _config;
private readonly ServiceBusClient _queueClient;
private readonly ServiceBusProcessor _processor;
// private readonly DataContext _context;
public ServiceBusConsumer(IConfiguration config,
// DataContext context)
{
_config = config;
// _context = context;
_queueClient = new ServiceBusClient(_config["ServiceBus:Connection"]);
_processor = _queueClient.CreateProcessor(_config["ServiceBus:Queue"], new ServiceBusProcessorOptions());
}
public void RegisterOnMessageHandlerAndReceiveMessages() {
_processor.ProcessMessageAsync += MessageHandler;
_processor.ProcessErrorAsync += ErrorHandler;
_processor.StartProcessingAsync();
}
private async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
JObject jsonObject = JObject.Parse(body);
var eventStatus = (string)jsonObject["EventStatus"];
await args.CompleteMessageAsync(args.Message);
// _context is disposed
// want to connect to DB here but don't know how!
// var ybEvent = _context.YogabandEvents.Where(p => p.ServiceBusSequenceNumber == args.Message.SequenceNumber).FirstOrDefault();
}
private Task ErrorHandler(ProcessErrorEventArgs args)
{
var error = args.Exception.ToString();
return Task.CompletedTask;
}
}
错误
无法访问已释放的上下文实例。此错误的常见原因是处理从依赖项注入中解析的上下文实例,然后尝试在应用程序的其他位置使用相同的上下文实例。如果对上下文实例调用‘Dispose’或将其包装在Using语句中,则可能会发生这种情况。如果您正在使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。 对象名称:‘DataContext’。
这里是Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
{
var context = services.GetRequiredService<DataContext>();
var userManager = services.GetRequiredService<UserManager<User>>();
var roleManager = services.GetRequiredService<RoleManager<Role>>();
var bus = services.GetRequiredService<IServiceBusConsumer>();
bus.RegisterOnMessageHandlerAndReceiveMessages();
}
catch (Exception ex)
{
var logger = loggerFactory.CreateLogger<Program>();
logger.LogError(ex, "An error occured during migration");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
这里是Startup.cs->;只是ConfigureServices方法
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingEvents));
services.AddAutoMapper(typeof(MappingMembers));
services.AddAutoMapper(typeof(MappingUsers));
services.AddAutoMapper(typeof(MappingYogabands));
services.AddAutoMapper(typeof(MappingReviews));
// objects being passed back to the UI. Before I was passing User/Photo/etc and they
// had loops/refrences back to the user objects
services.AddControllers().AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Error;
});
services.AddDbContext<DataContext>(x =>
// x.UseSqlite(_config.GetConnectionString("DefaultConnection"), y => y.UseNetTopologySuite()));
x.UseSqlServer(_config.GetConnectionString("SqlServerConnection"), y => y.UseNetTopologySuite()));
services.Configure<AuthMessageSenderOptions>(_config.GetSection("SendGrid"));
services.Configure<AuthMessageSenderOptionsNew>(_config.GetSection("SendGrid"));
services.Configure<ConfirmationOptions>(_config.GetSection("Confirmation"));
services.Configure<CloudinarySettings>(_config.GetSection("CloudinarySettings"));
services.AddApplicationServices();
services.AddIdentityServices(_config);
services.AddSwaggerDocumentation();
services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200");
});
});
}
以下是AddApplicationServices()
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
// scoped - better option when you want to maintain state within a request
// services.AddScoped<IEventConsumer, EventConsumer>();
services.AddScoped<IServiceBusProducer, ServiceBusProducer>();
services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();
services.AddScoped<IEmailSender, EmailSender>();
services.AddScoped<IEmailSender, EmailSenderNew>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>)));
services.AddScoped<LogUserActivity>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
var errors = actionContext.ModelState
.Where(e => e.Value.Errors.Count > 0)
.SelectMany(x => x.Value.Errors)
.Select(x => x.ErrorMessage).ToArray();
var errorResponse = new ApiValidationErrorResponse
{
Errors = errors
};
return new BadRequestObjectResult(errorResponse);
};
});
return services;
}
推荐答案
您的问题似乎出在DI上。
您的ServiceBusConsumer服务是单例的,但是您注入了一个DbContext作为构造函数。这通常是建议,但在这种情况下,它不起作用。
您在构造函数中注入一个DbContext,并将";保存&q;链接&q;到它。但随后它被释放,因此";link";将不起作用。
相反,您应该注入一个DbConextFactory。使用工厂,您可以按需创建DbContext实例。
private readonly IDbContextFactory<DataContext> _contextFactory;
public ServiceBusConsumer(IConfiguration config, IDbContextFactory<DataContext> contextFactory)
{
// Add this line
_contextFactory = contextFactory;
}
private async Task MessageHandler(ProcessMessageEventArgs args)
{
// With the new C# 8 syntax you can do
using var db = _contextFactory.CreateDbContext();
// Otherwise, wrap it up
using (var db = _contextFactory.CreateDbContext())
{
}
}
这里有一个指向文档的链接,其中显示了如何使用它:https://docs.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
您只需注册即可:
public void ConfigureServices(IServiceCollection services)
{
// Add this line to register a context factory
services.AddDbContextFactory<DataContext>(
options =>
.UseSqlServer(_config.GetConnectionString("SqlServerConnection"), y => y.UseNetTopologySuite()));
}
您不能使用与控制器相同的DI,因为它们通常不是单例,因此不会遇到这个问题。AFAIK DbConextFactory正是为此目的而创建的(考虑到Blazor)。如果您需要的服务不是DbContext,则需要在构造函数中注入服务提供程序,然后直接请求服务,尽管Microsoft不建议这样做。
这篇关于如果我的上下文被释放,在我收到来自Azure Service Bus侦听器/消费者的消息后,如何使用EF Core?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!