ขั้นแรกให้โหวต (อย่างน้อยที่สุด) คำตอบของอัลซามิ นั่นทำให้ฉันมาถูกทางแล้ว
แต่สำหรับคนที่คุณทำ IoC นี่เป็นการดำน้ำที่ลึกขึ้นเล็กน้อย
ข้อผิดพลาดของฉัน (เช่นเดียวกับข้อผิดพลาดอื่น ๆ )
เกิดข้อผิดพลาดอย่างน้อยหนึ่งรายการ (การดำเนินการที่สองเริ่มต้นในบริบทนี้ก่อนที่การดำเนินการก่อนหน้านี้จะเสร็จสมบูรณ์ซึ่งมักเกิดจากเธรดที่แตกต่างกันโดยใช้ DbContext อินสแตนซ์เดียวกันสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีหลีกเลี่ยงปัญหาเธรดกับ DbContext โปรดดูที่
https://go.microsoft.com / fwlink /? linkid = 2097913. )
การตั้งค่ารหัสของฉัน "แค่พื้นฐาน" ...
public class MyCoolDbContext: DbContext{
public DbSet <MySpecialObject> MySpecialObjects { get; set; }
}
และ
public interface IMySpecialObjectDomainData{}
และ (โปรดทราบว่ากำลังฉีด MyCoolDbContext)
public class MySpecialObjectEntityFrameworkDomainDataLayer: IMySpecialObjectDomainData{
public MySpecialObjectEntityFrameworkDomainDataLayer(MyCoolDbContext context) {
this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
}
}
และ
public interface IMySpecialObjectManager{}
และ
public class MySpecialObjectManager: IMySpecialObjectManager
{
public const string ErrorMessageIMySpecialObjectDomainDataIsNull = "IMySpecialObjectDomainData is null";
private readonly IMySpecialObjectDomainData mySpecialObjectDomainData;
public MySpecialObjectManager(IMySpecialObjectDomainData mySpecialObjectDomainData) {
this.mySpecialObjectDomainData = mySpecialObjectDomainData ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectDomainDataIsNull, (Exception)null);
}
}
และในที่สุดคลาสมัลติเธรดของฉันถูกเรียกจากแอพคอนโซล (แอพ Command Line Interface)
public interface IMySpecialObjectThatSpawnsThreads{}
และ
public class MySpecialObjectThatSpawnsThreads: IMySpecialObjectThatSpawnsThreads
{
public const string ErrorMessageIMySpecialObjectManagerIsNull = "IMySpecialObjectManager is null";
private readonly IMySpecialObjectManager mySpecialObjectManager;
public MySpecialObjectThatSpawnsThreads(IMySpecialObjectManager mySpecialObjectManager) {
this.mySpecialObjectManager = mySpecialObjectManager ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectManagerIsNull, (Exception)null);
}
}
และการสะสม DI (อีกครั้งสำหรับแอปพลิเคชันคอนโซล (อินเทอร์เฟซบรรทัดคำสั่ง) ... ซึ่งแสดงพฤติกรรมที่แตกต่างจากเว็บแอปเล็กน้อย)
private static IServiceProvider BuildDi(IConfiguration configuration) {
string defaultConnectionStringValue = string.Empty;
IServiceCollection servColl = new ServiceCollection()
.AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
.AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()
# if (MY_ORACLE)
.AddDbContext<ProvisioningDbContext>(options => options.UseOracle(defaultConnectionStringValue), ServiceLifetime.Transient);
# endif
# if (MY_SQL_SERVER)
.AddDbContext<ProvisioningDbContext>(options => options.UseSqlServer(defaultConnectionStringValue), ServiceLifetime.Transient);
# endif
servColl.AddSingleton <IMySpecialObjectThatSpawnsThreads, MySpecialObjectThatSpawnsThreads>();
ServiceProvider servProv = servColl.BuildServiceProvider();
return servProv;
}
สิ่งที่ทำให้ฉันประหลาดใจคือ (เปลี่ยนเป็น) ชั่วคราวสำหรับ
.AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
.AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()
หมายเหตุฉันคิดว่าเนื่องจาก IMySpecialObjectManager ถูกฉีดเข้าไปใน "MySpecialObjectThatSpawnsThreads" วัตถุที่ถูกฉีดเหล่านั้นจำเป็นต้องเป็นแบบชั่วคราวเพื่อให้ห่วงโซ่สมบูรณ์
ประเด็นคือ ....... มันไม่ใช่แค่ DbContext (ของฉัน) ที่ต้องการ Transient ... แต่เป็น DI Graph ที่ใหญ่กว่า
เคล็ดลับการแก้จุดบกพร่อง:
บรรทัดนี้:
this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
ใส่จุดพักดีบั๊กไว้ที่นั่น หาก MySpecialObjectThatSpawnsThreads ของคุณสร้าง N จำนวนเธรด (เช่น 10 เธรดเช่น) ...... และบรรทัดนั้นถูกตีเพียงครั้งเดียว ... นั่นคือปัญหาของคุณ DbContext ของคุณกำลังข้ามเธรด
โบนัส:
ฉันขอแนะนำให้อ่าน URL / บทความด้านล่างนี้ (oldie but goodie) เกี่ยวกับความแตกต่างของเว็บแอปและคอนโซลแอป
https://mehdi.me/ambient-dbcontext-in-ef6/
นี่คือส่วนหัวของบทความในกรณีที่ลิงก์เปลี่ยนไป
การจัดการ DBCONTEXT วิธีที่ถูกต้องด้วย ENTITY FRAMEWORK 6: คู่มือเชิงลึก Mehdi El Gueddari
ฉันพบปัญหานี้กับ WorkFlowCore https://github.com/danielgerlag/workflow-core
<ItemGroup>
<PackageReference Include="WorkflowCore" Version="3.1.5" />
</ItemGroup>
โค้ดตัวอย่างด้านล่าง .. เพื่อช่วยเหลือผู้ค้นหาทางอินเทอร์เน็ตในอนาคต
namespace MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Workflows
{
using System;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Constants;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Glue;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.WorkflowSteps;
using WorkflowCore.Interface;
using WorkflowCore.Models;
public class MySpecialObjectInterviewDefaultWorkflow : IWorkflow<MySpecialObjectInterviewPassThroughData>
{
public const string WorkFlowId = "MySpecialObjectInterviewWorkflowId";
public const int WorkFlowVersion = 1;
public string Id => WorkFlowId;
public int Version => WorkFlowVersion;
public void Build(IWorkflowBuilder<MySpecialObjectInterviewPassThroughData> builder)
{
builder
.StartWith(context =>
{
Console.WriteLine("Starting workflow...");
return ExecutionResult.Next();
})
.Then(lastContext =>
{
Console.WriteLine();
bool wroteConcreteMsg = false;
if (null != lastContext && null != lastContext.Workflow && null != lastContext.Workflow.Data)
{
MySpecialObjectInterviewPassThroughData castItem = lastContext.Workflow.Data as MySpecialObjectInterviewPassThroughData;
if (null != castItem)
{
Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete :) {0} -> {1}", castItem.PropertyOne, castItem.PropertyTwo);
wroteConcreteMsg = true;
}
}
if (!wroteConcreteMsg)
{
Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete (.Data did not cast)");
}
return ExecutionResult.Next();
}))
.OnError(WorkflowCore.Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(60));
}
}
}
และ
ICollection<string> workFlowGeneratedIds = new List<string>();
for (int i = 0; i < 10; i++)
{
MySpecialObjectInterviewPassThroughData currentMySpecialObjectInterviewPassThroughData = new MySpecialObjectInterviewPassThroughData();
currentMySpecialObjectInterviewPassThroughData.MySpecialObjectInterviewPassThroughDataSurrogateKey = i;
string wfid = await this.workflowHost.StartWorkflow(MySpecialObjectInterviewDefaultWorkflow.WorkFlowId, MySpecialObjectInterviewDefaultWorkflow.WorkFlowVersion, currentMySpecialObjectInterviewPassThroughData);
workFlowGeneratedIds.Add(wfid);
}