使用EFCore多线程将Oracle数据库迁移到Mysql数据库

最近要写一个从ORACLE数据库转存到Mysql数据库的程序,特别在使用多线程的时候,出现了一些问题。特此记录一下。

首先,在控制台Main方法里面进行依赖注入。

这里要注意的是oracle数据库在注入数据库链接字符串的时候要指定版本(serviceProvider.AddDbContext<ModelContext>(options => options.UseOracle(oracleString, opts => opts.UseOracleSQLCompatibility("11")), ServiceLifetime.Transient);)不然在使用某些方法(比如skip和take)的时候会报错。

因为要在多线程中使用,所以都指定了瞬时模式ServiceLifetime.Transient

static void Main(string[] args)
{
try
{
string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
string configFilePath = assemblyDirPath + "\\appsettings.json";
var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(configFilePath).Build();
string conString = configuration.GetConnectionString("DefaultConnection");
string oracleString = configuration.GetConnectionString("OracleConnection");

var serviceProvider = new ServiceCollection();
serviceProvider.AddLogging();
serviceProvider.AddDbContext<XXXXContext>(options => options.UseMySql(conString),ServiceLifetime.Transient);
serviceProvider.AddDbContext<ModelContext>(options => options.UseOracle(oracleString, opts => opts.UseOracleSQLCompatibility("11")), ServiceLifetime.Transient);
serviceProvider.AddTransient<IRepository, Repository>();
serviceProvider.AddTransient<XXX_Service>();
serviceProvider.AddTransient<YYYY_Service>();
var service = serviceProvider.BuildServiceProvider();
using(var scope = service.CreateScope())
{
YYYY yy1 = service.GetService<YYYY_Service>();
yy1.OnStart();
}
}
catch (Exception ex)
{
Log4Helper.Error(ex.Message, ex);
}
}

其次,在操作类依赖注入的时候。最先我是直接在构造函数里直接注册了IRepository,然后在多线程的时候报错:Cannot set MySqlCommand.CommandText when there is an open DataReader for this command; it must be closed first,然后我将IRepository里注册的ORACLE和MYSQL 的hashcode打印出来,发现原来是一样的,这就说明,由于多线程部分使用的都是同一个IRepository,这导致了大家使用的都是同一个DBContext,于是,我对代码进行了一些改变,即改为注入IServiceProvider ,然后在多线程部分再每次都重新生成一个IRepository。结果成功解决问题。

public IServiceProvider _service { get; }
public YYYY_Service(IServiceProvider service)
{
_service = service;
}

public void OnStart()
{
List<Task> tasks = new List<Task>();
int count = 0;
while (true)
{
IRepository service = (IRepository)_service.GetService(typeof(IRepository));
var list = service.GetOracleSkipTake<hehe>(1000 * count, 1000);
if (!list.Any())
{
Log4Helper.Info("完成!");
var tableUpdate = service.GetMysqlData<SysTime>(c => c.Tablename == "ggg").First();
tableUpdate.Gxsj = DateTime.Now;
service.UpdateMysql(tableUpdate);
break;
}

//Trans(list);
tasks = tasks.Where(c => !c.IsCanceled && !c.IsCompleted && !c.IsFaulted).ToList();
if (tasks.Count() >= 5)
{
Task.WaitAny(tasks.ToArray());
}
tasks.Add(Task.Factory.StartNew(() => Trans(list)));
count++;
}

}

public void Trans(IList<NNNN> list)
{
 IRepository service = (IRepository)_service.GetService(typeof(IRepository));

}

结论说明,瞬时模式虽然在每次请求的时候都会重新生成实例,但是,由于大家用的都是同一个IRepository,结果在每次通过IRepository调用方法的时候,他并没有进入构造函数,所以不会生成新的DbContext,所以这就会导致冲突报错!

猜你喜欢

转载自www.cnblogs.com/caiyi168/p/12787450.html