cmddependency-injectionsqldependency

BackgroundService memory leak SqlDependency


this is my service code

public class Worker : BackgroundService
{
    public bool isRegister { get; set; }
    public bool checkIp { get; set; }
    public long timePass { get; set; }

    public Worker()
    {
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        return base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (isRegister == false)
                registerSelect();
            if (checkIp == true)
            {
                checkIp = false;
                await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();
            }
            timePass += 1000;
            if (timePass % 60000 == 0)
                await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();

            await Task.Delay(1000, stoppingToken);
        }
    }

    public void registerSelect()
    {
        isRegister = true;
        using (SqlConnection conn = new SqlConnection(GetDbConnection()))
        {
            conn.Open();

            SqlDependency.Start(GetDbConnection());

            string commandText = "SELECT [Ip1],[Ip2] ,[Ip3] ,[Ip4] FROM dbo.BlockFirewallIps where IsRead is null";

            using (SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                SqlDependency dependency = new SqlDependency(cmd);

                dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);

                cmd.ExecuteNonQuery();
            }
            conn.Close();
        }
    }

    void OnDependencyChange(object sender, SqlNotificationEventArgs e)
    {
        if (e.Info == SqlNotificationInfo.Insert)
            checkIp = true;
        SqlDependency temp = sender as SqlDependency;
        if (temp != null)
            temp.OnChange -= new OnChangeEventHandler(OnDependencyChange);

        registerSelect();
    }

    private string GetDbConnection()
    {
        return GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"];
    }
}

and this is my IBlockFirewallIpService.RegisterInFirewall() code

 public async Task RegisterInFirewall()
    {
        var allBlockIps = await db.BlockFirewallIps.Where(t => t.IsRead == null).ToListAsync();
        foreach (var ip in allBlockIps)
        {
            BanIP("OjeFirTCP" + ip.Ip1 + "_" + ip.Ip2 + "_" + ip.Ip3 + "_" + ip.Ip4, ip.Ip1 + "." + ip.Ip2 + "." + ip.Ip3 + "." + ip.Ip4, "Any", "TCP");
            BanIP("OjeFirUDP" + ip.Ip1 + "_" + ip.Ip2 + "_" + ip.Ip3 + "_" + ip.Ip4, ip.Ip1 + "." + ip.Ip2 + "." + ip.Ip3 + "." + ip.Ip4, "Any", "UDP");
            ip.IsRead = true;
            db.SaveChanges();
        }
    }

    void BanIP(string RuleName, string IPAddress, string Port, string Protocol)
    {
        if (OperatingSystem.IsWindows())
        {
            if (!string.IsNullOrEmpty(RuleName) && !string.IsNullOrEmpty(IPAddress) && !string.IsNullOrEmpty(Port) && !string.IsNullOrEmpty(Protocol) && new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator))
            {
                using (Process RunCmd = new Process())
                {
                    RunCmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    RunCmd.StartInfo.FileName = "cmd.exe";
                    RunCmd.StartInfo.Arguments = "/C netsh advfirewall firewall add rule name=\"" + RuleName + "\" dir=in action=block remoteip=" + IPAddress + " remoteport=" + Port + " protocol=" + Protocol;
                    RunCmd.Start();
                }
            }
        }
    }

this is progeram.cs

IHost host = Host.CreateDefaultBuilder(args)
 .UseWindowsService(options =>
 {
     options.ServiceName = "OjeFirewall";
 })
.ConfigureServices((hostContext, services) =>
{
    GlobalConfig.Configuration = hostContext.Configuration;
    services.AddScoped<IHttpContextAccessor, FakeIHttpContextAccessor>();
    SecurityConfig.Config(services);
    services.AddHostedService<Worker>();
})
.Build();

await host.RunAsync();

this is SecurityConfig.Config codes

 services.AddDbContext<SecurityDBContext>(options =>
                options.UseSqlServer(GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"],
                b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
                , ServiceLifetime.Singleton
        );

        services.AddSingleton<IIpLimitationWhiteListService, IpLimitationWhiteListService>();
        services.AddSingleton<IIpLimitationBlackListService, IpLimitationBlackListService>();
        services.AddSingleton<IFileAccessRoleService, FileAccessRoleService>();
        services.AddSingleton<IRoleService, RoleService>();
        services.AddSingleton<IBlockClientConfigService, BlockClientConfigService>();
        services.AddSingleton<IBlockAutoIpService, BlockAutoIpService>();
        services.AddSingleton<IBlockFirewallIpService, BlockFirewallIpService>();

the problem :

this code using too much memory after 3 day starting ram usage is 20mb after first call (OnDependencyChange) it use 47mb after 3 day it use 178mb

where i am doing wrong ?!


Solution

  • i found problem

    await SecurityConfig.cacheServices?.BuildServiceProvider()?.GetService<IBlockFirewallIpService>().RegisterInFirewall();
    

    this line code was the problem after change it to normal injection ram usage is stable now, but i can not understand whay !?