【发布时间】:2020-04-17 15:40:01
【问题描述】:
我想询问拥有控制台应用程序的最佳方法,该应用程序也可以在网络核心环境中用作 Windows 服务。问题不在于拥有这样的应用程序,而在于执行的代码。 我试图解释我的问题到底是什么。
如果windows-service启动了,就会启动一个for循环,它会做几件事
- 访问亚马逊 AWS SQS
- 通过 HTTP 请求访问 csv 文件 => 正在使用这些数据并部分存储在 db 中
- 通过 EF 访问 oracle db 的表(插入、更新和删除)
到目前为止一切顺利。一切都按我的意愿进行。使用依赖注入(Scoped)在我的循环中访问我为完成所有操作而编写的那些方法。
棘手的部分是这个应用程序的内存使用量相当......不是最佳的。它做了它的工作,但是在下载和使用 csv 文件的数据时,应用程序使用了太多的内存并且没有正确释放。您对如何处理此类场景(windows-service 中的循环)有任何建议或知识库文章吗?
我试图释放我所能做的一切,比如清除列表并将它们设置为 null,在查询数据时禁用 EF 中的任何跟踪(也禁用额外的插入/更新 changetracker)和使用“使用语句”(/处置元素)。
另外,我想补充一点,我正在使用最新的 Amazon AWS 开发工具包(Core 和 SQS)和 EF Core 2.2.6 以及 Oracle Provider。
你有什么提示吗?
如果您需要更多信息,请告诉我。然后我会根据需要提供更多数据。
亲切的问候
关于读取csv文件的注释
从 URL 读取文件。
using (var response = await request.GetResponseAsync())
{
await using (var receiveStream = response.GetResponseStream())
{
using (var readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
var content = readStream.ReadToEnd();
result.Content = content.Split('\n').ToList();
result.IsSuccess = true;
}
}
}
阅读后,我将其转换为我的目标类
public static async Task<List<Curve>> ReturnCurveData(List<string> content)
{
var checkVar = -1;
var list = new List<Curve>();
foreach (var entry in content)
{
if (string.IsNullOrEmpty(entry)) continue;
var entrySplitted = entry.Split('|');
if (entrySplitted.Length < 3) continue;
else if(!int.TryParse(entrySplitted[0], out checkVar)) continue;
var item = new Curve();
item.Property1 = Convert.ToInt32(entrySplitted[0]);
item.Property2 = (entrySplitted.Length ==3) ? DateTime.Now : Convert.ToDateTime(entrySplitted[1]);
item.Property3 = (entrySplitted.Length ==3) ? Convert.ToDateTime(entrySplitted[1]) : Convert.ToDateTime(entrySplitted[2]);
item.Value = (entrySplitted.Length ==3) ? Convert.ToDecimal(entrySplitted[2], new CultureInfo("en-US")): Convert.ToDecimal(entrySplitted[3], new CultureInfo("en-US"));
list.Add(item);
}
return await Task.FromResult(list);
}
关于范围的定义
var hostBuilder = new HostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
...
})
.ConfigureServices((hostContext, services) =>
{
services.AddScoped<Data.Queries.Database.Db>();
services.AddScoped<Data.Queries.External.Aws>();
services.AddScoped<Data.Queries.External.Web>();
services.RegisterEfCoreOracle<DbContext>(AppDomain.CurrentDomain.BaseDirectory,
"cfg_db.json");
services.AddScoped<IExecute, Execute>();
services.AddHostedService<ExecuteHost>();
})
.ConfigureLogging((hostingContext, logging) =>
{
...
});
public static void RegisterEfCoreOracle<T>(this IServiceCollection services, string configurationDirectory, string configurationFile, ServiceLifetime lifetime = ServiceLifetime.Scoped) where T : DbContext
{
//Adding configuration file
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(configurationDirectory)
.AddJsonFile(configurationFile, optional: false)
.Build();
services.Configure<OracleConfiguration<T>>(configuration);
var oraConfig = services.ReturnServiceProvider().GetService<IOptions<OracleConfiguration<T>>>();
if (oraConfig.Value.Compatibility != null)
{
services.AddDbContext<T>(o => o
.UseOracle(oraConfig.Value.ConnectionString(), b => b
.UseOracleSQLCompatibility(oraConfig.Value.Compatibility)), lifetime);
}
else
{
services.AddDbContext<T>(o => o
.UseOracle(oraConfig.Value.ConnectionString()), lifetime);
}
}
【问题讨论】:
-
听起来您正在将整个 .csv 文件加载到内存中,这是您永远不应该这样做的。但是,如果没有代码或分析支持,很难说。
-
同意此评论。如果可能,您应该使用 Stream。但是,如果你不能这样做,你可以释放对象并强制 GC。不明白为什么你不能流式传输
-
我更新了我的初始帖子。如果从您的角度来看不是最好的方法,也许您可以告诉您如何以不同的方式进行操作? @盲人
-
如何定义范围?您不想将单个 EF DbContext 保留很长时间。 ASP.NET 通过为每个请求定义范围来为您做到这一点。但在长期运行的服务中,您要么必须创建自己的范围,要么手动管理 DbContext 的生命周期。
-
@DavidBrowne-Microsoft 我编辑了我的原始帖子。我希望这能澄清我所说的“范围”的含义。
标签: c# performance entity-framework .net-core