Mat*_*ski 4 .net c# entity-framework dbcontext .net-core
我正在尝试从服务调用存储库中的方法。
上下文、存储库和服务都被定义为范围服务。
这是我首先调用的方法:
public async void ReceiveWebhook(HttpContext httpContext)
{
// some unimportant checks here
var productPurchaseRequest = new ProductPurchaseRequest
{
Amount = Convert.ToInt32(result?.Quantity),
Timestamp = DateTime.Now,
ProductType = productType,
PaymentProviderOrderId = Convert.ToInt32(result?.OrderId),
PaymentProviderProductId = Convert.ToInt32(result?.ProductId),
PaymentProviderTransactionId = result?.TransactionId!,
PaymentModel = PaymentModel.Subscription,
PhoneNumber = result?.Passthrough!
//todo: change payment model
};
var bought = await _productProvisioningRepository.PurchaseProduct(productPurchaseRequest);
}
Run Code Online (Sandbox Code Playgroud)
这是方法:PurchaseProduct()在存储库中:
public async Task<bool> PurchaseProduct(ProductPurchaseRequest productPurchaseRequest)
{
await using var transactionScope = await _context.Database.BeginTransactionAsync();
var query = from u in _context.signumid_user
where u.PhoneNumber == productPurchaseRequest.PhoneNumber
select u;
var user = await query.FirstOrDefaultAsync();
if (user == null)
{
return false;
//todo: log user with phone number does not exist
}
try
{
var transaction = new Transaction
{
Timestamp = productPurchaseRequest.Timestamp,
PaymentProviderOrderId = productPurchaseRequest.PaymentProviderOrderId,
PaymentProviderProductId = productPurchaseRequest.PaymentProviderProductId,
PaymentProviderTransactionId = productPurchaseRequest.PaymentProviderTransactionId
};
var transactionDb = await _context.signumid_transaction.AddAsync(transaction);
await _context.SaveChangesAsync();
var productEntry = new ProductEntry
{
Amount = productPurchaseRequest.Amount,
ExpiryDate = productPurchaseRequest.Timestamp.AddMonths(1),
PaymentModel = (int) PaymentModel.Subscription,
ProductType = productPurchaseRequest.ProductType,
UserId = 1
};
var productEntryDb = await _context.signumid_product_entry.AddAsync(productEntry);
await _context.SaveChangesAsync();
var transactionProductEntry = new Transaction_ProductEntry
{
TransactionId = transactionDb.Entity.Id,
ProductEntryId = productEntryDb.Entity.Id
};
var transactionProductEntryDb = await _context.sisgnumid_transaction_product_entry.AddAsync(transactionProductEntry);
await _context.SaveChangesAsync();
//todo: check if everything is okay with database entries
await transactionScope.CommitAsync();
return true;
}
catch (Exception e)
{
// todo: add log
Console.WriteLine(e);
await transactionScope.RollbackAsync();
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
这是program.cs文件:
using System.Diagnostics;
using System.Reflection;
using System.Text.Json.Serialization;
using FluentMigrator.Runner;
using Hangfire;
using Hangfire.PostgreSql;
using Hangfire.SQLite;
using Hangfire.SqlServer;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DataEncryption;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Newtonsoft.Json;
using Serilog;
using Serilog.Exceptions;
using Serilog.Exceptions.Core;
using Signumid.ExceptionMiddleware;
using Signumid.Global;
using Signumid.MigratorRunner;
using Signumid.ProductProvisioning;
using Signumid.ProductProvisioning.Migrations;
var builder = WebApplication.CreateBuilder(args);
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(builder.Environment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
var configurationRoot = configurationBuilder.Build();
ConfigureLogging(configurationRoot);
var applicationSettings = new Signumid.ApplicationSettings.ApplicationSettings();
configurationRoot.Bind(applicationSettings);
SignumIdGlobal.InitialiseApplicationSettings(applicationSettings);
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.PropertyNameCaseInsensitive = false;
options.SerializerOptions.PropertyNamingPolicy = null;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
MigratorRunner.MigrateDatabase(builder.Services,
SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString,
SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider,
Assembly.GetAssembly(typeof(InitialMigration)));
builder.Services.AddScoped<ProductProvisioningContext>();
builder.Services.AddScoped<ProductProvisioningRepository>();
builder.Services.AddScoped<ProductProvisioningService>();
ConfigureHangfire(builder.Services);
ConfigureHealthCheck(builder.Services);
builder.Services.AddCors(options =>
options.AddPolicy("CorsPolicy",
corsPolicyBuilder =>
{
corsPolicyBuilder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.SetIsOriginAllowed(_ => true)
.WithExposedHeaders("Content-Disposition", "Content-Length");
}));
builder.Services.AddAuthorization();
var app = builder.Build();
// Configure hangfire to use the new JobActivator we defined.
// Use the previously configured CorsPolicy policy
app.UseCors("CorsPolicy");
app.ConfigureExceptionHandler();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.MapHealthChecks("/v/1/health/basic");
app.MapHealthChecks("/v/1/health/simplified",
new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
status = report.Status.ToString(),
monitors = report.Entries.Select(e => new
{key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status)})
}));
}
}
).RequireHost(SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.HealthCheckHosts);
app.MapGet("/v/1/signer/getAvailableSignatures",
(string phoneNumber, int productType, ProductProvisioningService service) => service.RetrieveRemainingProductAmountForUser(phoneNumber, productType));
app.MapGet("/v/1/signer/generatePayLink",
(string phoneNumber, int quantity, int productId, ProductProvisioningService service) => service.GeneratePayLink(phoneNumber, quantity, productId));
app.MapPost("/v/1/signer/receiveWebhook",
(HttpContext context, ProductProvisioningService service) => service.ReceiveWebhook(context));
app.UseAuthorization();
app.UseHangfireDashboard();
GlobalConfiguration.Configuration
.UseActivator(new HangfireActivator(app.Services));
var runner = app.Services.GetRequiredService<IMigrationRunner>();
// Execute the migrations
runner.MigrateUp();
app.Run();
static void ConfigureLogging(IConfigurationRoot configuration)
{
try
{
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
Serilog.Debugging.SelfLog.Enable(Console.Error);
// Impossible to set with appsettings:
// /sf/ask/4101136301/#58622735
// https://github.com/RehanSaeed/Serilog.Exceptions/issues/58
var loggingConfiguration = new LoggerConfiguration()
.Enrich
.WithExceptionDetails(new DestructuringOptionsBuilder().WithDefaultDestructurers())
.ReadFrom
.Configuration(configuration);
Log.Logger = loggingConfiguration
.CreateLogger();
}
catch (Exception e)
{
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
Serilog.Debugging.SelfLog.Enable(Console.Error);
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.MinimumLevel.Debug() // set to minimal in serilog
.CreateLogger();
Log.Debug(e,
"Unable to import serilog configuration from appsettings.json, logging only to console. Error: {@Ex}", e);
}
var currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += UnhandledExceptionHandler;
}
static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
var e = (Exception) args.ExceptionObject;
Log.Fatal(e, "Unhandled exception caught : {@error}", e);
Log.Fatal("Runtime terminating: {0}", args.IsTerminating);
}
static void ConfigureHealthCheck(IServiceCollection services)
{
if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals("SqlLite",
StringComparison.InvariantCultureIgnoreCase))
{
services.AddHealthChecks()
.AddSqlite(SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString);
}
else if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals(
"postgresql",
StringComparison.InvariantCultureIgnoreCase))
{
services.AddHealthChecks()
.AddNpgSql(SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString);
}
else if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals(
"sqlserver",
StringComparison.InvariantCultureIgnoreCase))
{
services.AddHealthChecks()
.AddSqlServer(SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString);
}
else
{
services.AddHealthChecks();
}
}
static void ConfigureHangfire(IServiceCollection services)
{
services.AddHangfire(configuration =>
{
configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings();
});
if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals("SqlLite",
StringComparison.InvariantCultureIgnoreCase))
{
GlobalConfiguration.Configuration.UseSQLiteStorage(SignumIdGlobal.ApplicationSettings
.ProductProvisioningApplicationSettings
.ConnectionString, new SQLiteStorageOptions
{
SchemaName = "product_provisioning"
});
}
else if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals(
"postgresql",
StringComparison.InvariantCultureIgnoreCase))
{
GlobalConfiguration.Configuration.UsePostgreSqlStorage(
SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString,
new PostgreSqlStorageOptions
{
SchemaName = "product_provisioning"
});
}
else if (SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.DatabaseProvider.Equals(
"sqlserver",
StringComparison.InvariantCultureIgnoreCase))
{
GlobalConfiguration.Configuration.UseSqlServerStorage(
SignumIdGlobal.ApplicationSettings.ProductProvisioningApplicationSettings.ConnectionString,
new SqlServerStorageOptions
{
SchemaName = "product_provisioning"
});
}
services.AddHangfireServer();
}
Run Code Online (Sandbox Code Playgroud)
当我声明事务范围时,我在方法的第一行中收到错误。错误如下:
无法访问已释放的上下文实例。导致此错误的一个常见原因是处置从依赖项注入解析的上下文实例,然后尝试在应用程序的其他位置使用相同的上下文实例。如果您在上下文实例上调用“Dispose”或将其包装在 using 语句中,则可能会发生这种情况。如果您使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。对象名称:“ProductProvisioningContext”。
你的ReceiveWebhook是async void,将其更改为 return Task:
public async Task ReceiveWebhook(HttpContext httpContext)
Run Code Online (Sandbox Code Playgroud)
否则,ASP.NET Core 无法等到处理结束,并将在处理程序完成之前完成请求(包括处理创建的范围和一次性依赖项,例如数据库上下文)。
阅读更多:
| 归档时间: |
|
| 查看次数: |
3564 次 |
| 最近记录: |