I have the following base class:
public class ReportBase {
protected List<ReportDataTable> ReportDataTables { get;set; }
}
We have this report base being used on multiple report classes - such as this:
public class MyCustomReport : ReportBase {
public MyCustomReport() {}
public void GenerateReport(string filters) {
ReportDataTables = new List<ReportDataTable>();
//etc etc etc
}
}
We are using autofac to dynamically swap out these various reports as needed for generation.
public class ReportService : IReportService
{
private readonly IEnumerable<Lazy<IReportGeneratorService, IReportGeneratorMetadata>> _reportGeneratorServices;
public ReportService(IEnumerable<Lazy<IReportGeneratorService, IReportGeneratorMetadata>> reportGeneratorServices)
{
_reportGeneratorServices = reportGeneratorServices;
}
public TestMethod(){
//Not thread safe when called inside a Parallel.ForEach
var reportServiceLazyLoad = _reportGeneratorServices.FirstOrDefault(s => s.Metadata.Name == myMetaDataName);
}
}
the problem we are encountering is that when we run a parallel.foreach and call the "TestMethod" for the same report type with different filters the underlying ReportDataTables is populated with data from multiple reports instead of specifically for the one being called. I'm not sure how to segment it so that it only loads up data for that specific service, it seems that when its in a parallel for each its mixing in results from other threads or something.
Not sure if this makes sense, the above code isn't exact but generalized to give a general idea. i can clarify more if necessary - thanks for all your help.
More Details To Illustrate the Problem
the code below i believe is illustrating the issue i'm encountering. I'm trying to dynamically pull the service out of the collection but i need it to be a new instance, not a reference, the problem is that when this occurs inside a parallel.foreach its using the same reference and updating data inaccurately, if it was using a seperate isolated instance i wouldn't have data being mixed across - if this makes sense?
public class Test {
private readonly IEnumerable<Lazy<IReportGeneratorService, IReportGeneratorMetadata>> _reportGeneratorServices;
public Test(IEnumerable<Lazy<IReportGeneratorService, IReportGeneratorMetadata>> reportGeneratorServices)
{
_reportGeneratorServices = reportGeneratorServices;
}
public void CheckRefEquality(){
var t1 = _reportGeneratorServices.FirstOrDefault(s => s.Metadata.Name == "MyReportService");
var t2 = _reportGeneratorServices.FirstOrDefault(s => s.Metadata.Name == "MyReportService");
var t3 = Object.ReferenceEquals(t1.Value, t2.Value);
//t3 returns true - how can i forcibly create a new instance instead of pulling the same instance, this is what i believe is causing issues.
}
}
Update per Erics Example
In your sample code below:
fooService.GitErDone("Bar", "filter_1");
fooService.GitErDone("Bar", "filter_2");
fooService.GitErDone("Baz", "filter_3");
fooService.GitErDone("Bar", "filter_4");
fooService.GitErDone("Baz", "filter_5");
you said the above creates two instances, one per registration
what i'm trying to do is essentially the equivilanet of this - assuming each of the calls below happen concurrently in a parallel.foreach:
fooService.GitErDone("ReportService1", "filter_1");
fooService.GitErDone("ReportService1", "filter_2");
fooService.GitErDone("ReportService1", "filter_3");
fooService.GitErDone("ReportService1", "filter_4");
fooService.GitErDone("ReportService1", "filter_5");
Where essentially every one of the calls above uses a unique instance of ReportService1 - the problem is that when its sharing the instance it appears the underlying base class collection "ReportDataTables" is getting mixed data from different filters.
This may be a better representation
Parallel.ForEach(filters, f => {
var report = _reportsCollection.FirstOrDefault(s => s.Metadata.Name == "AlwaysTheSameReport");
// report should be a new instance, however when i run this in reality its always the same instance instead of a new one
report.GetErDone(f);
});