.net core with DI

Dear RemObjects Support,

I am trying to integrate RemObjects DataAbstract into a standard .NET Core RazorPages template project. The integration works well with Microsoft Dependency Injection, and the server starts successfully. Below is the code I use for the integration:

using RemObjects.DataAbstract.Server;
using RemObjects.SDK.Extensions;
using WebRodaApp;
using WebRodaApp.Services;

[assembly: HostingStartup(typeof(ConfigureRODA))]

namespace WebRodaApp;

public class ConfigureRODA : IHostingStartup
{
	public void Configure(IWebHostBuilder builder)
	{
		builder
			.ConfigureServices((context, services) => 
			{
				services
					.AddHostedService<RodaWorker>()
					.AddInfrastructureServices<AppServerEngine>(HostExtensions.GetAppServerConfiguration(configuration => 
					{
						configuration.ApplicationName = "WebRodaApp";
						configuration.RodlNamespace = "WebRodaApp";
					}))
					.AddApplicationServices()
					.AddNetworkServerConfiguration(server =>
					{
						server.Port = 8099;
					})
					.AddDataAbstract(context.Configuration);
            });
	}
}

public static class DataAbstractExtensions
{
	public static IServiceCollection AddDataAbstract(this IServiceCollection services, IConfiguration configuration)
	{
		if (Configuration.Loaded) return services;

		Configuration.Load();
		
		var connectionManager = new ConnectionManager();
		
		var connectionString = configuration.GetValue<string>("DataAbstract:ConnectionsDb");
		connectionManager.AddDefinition("Connections", connectionString);

		var service = Engine.AcquireLocalService(nameof(ConnectionsService), Guid.Empty) as ConnectionsService;
		service?.SetConnections();
		
		return services;
	}
}

internal sealed class RodaWorker(IAppServerEngine engine) : BackgroundService
{
	protected override async Task ExecuteAsync(CancellationToken stoppingToken)
	{
		engine.Initialize();
		engine.Start();

		try
		{
			while (!stoppingToken.IsCancellationRequested)
			{
				await Task.Delay(1000, stoppingToken);
			}
		}
		finally
		{
			engine.Stop();
		}
	}
}

The challenge arises when I attempt to call a method defined in a DataAbstract service (ConnectionsService) directly from the server application. According to the documentation, I need to use AcquireLocalService, which works as long as the service constructor is parameterless. However, when I modify the service constructor to include dependencies (e.g., ILogger), I encounter an error indicating that a parameterless constructor is required.

Below is the code for my ConnectionsService implementation:

using RemObjects.DataAbstract.Linq;
using RemObjects.DataAbstract.Server;
using RemObjects.SDK.Server;
using WebRodaApp.Models;
using Engine = RemObjects.DataAbstract.Server.Engine;


namespace WebRodaApp.Services
{
    [Service]
    public class ConnectionsService: DataAbstractService
    {
        private readonly ILogger? _logger;

        public ConnectionsService(ILogger logger): this()
        {
            _logger = logger;
        }
        
        public ConnectionsService()
        {
            AcquireConnection = true;
            ServiceDataStreamer = new RemObjects.DataAbstract.Bin2DataStreamer();
            ServiceSchemaName = "Connections";
        }


        public List<(string name, string path)> GetConnections()
        {
            _logger?.Log("GetConnections called");
            
            var lda = new LinqLocalDataAdapter(this, Guid.Empty);
            var result = from c in lda.GetTable<databases>()                         
                         select new { c.name, c.path };

            return result.Map(x => (x.name, x.path));
        }
        
        [ServiceMethod]
        public void SetConnections()
        {
            _logger?.Log("SetConnections called");
            
            var connectionString = Engine.ConnectionManager.ConnectionDefinitions.FindItem("Connections").ConnectionString;

            foreach (var conn in GetConnections())
            {
                var found = Engine.ConnectionManager.ConnectionDefinitions.FindItem(conn.name);
                if (found != null) continue;
                
                Engine.ConnectionManager.AddDefinition(conn.name, connectionString.ReplaceDatabase(conn.path));
            }
        }
    }
}

My goal is to use Dependency Injection for service dependencies, such as ILogger, while still being able to call the service from within the server application.

Additionally, can you confirm if I am correctly integrating DataAbstract (and SDK) with the ConfigureRODA class?

Thank you in advance for your assistance!

Best regards,

Dejan

I found that calling AcquireLocalService right after engine.Start() in the RodaWorker class allowed me to successfully obtain the service instance with the logger correctly injected.

Here’s a simplified snippet of the solution for clarity:

internal sealed class RodaWorker(IAppServerEngine engine) : BackgroundService
{
	protected override Task ExecuteAsync(CancellationToken stoppingToken)
	{
		engine.Initialize();
		engine.Start();
		
		var service = Engine.AcquireLocalService(nameof(ConnectionsService), Guid.Empty) as ConnectionsService;
		service?.AddNewConnections();
		
		stoppingToken.Register(engine.Stop);

		return Task.CompletedTask;
	}
}

Could you kindly confirm if this is the recommended approach for obtaining and working with services in this context?

Thanks for your support!

Hi,

I think you may need to call Engine.ReleaseLocalService too like

internal sealed class RodaWorker(IAppServerEngine engine) : BackgroundService
{
	protected override Task ExecuteAsync(CancellationToken stoppingToken)
	{
		engine.Initialize();
		engine.Start();
		
		var service = Engine.AcquireLocalService(nameof(ConnectionsService), Guid.Empty) as ConnectionsService;
		try {
			service?.AddNewConnections();
		
			stoppingToken.Register(engine.Stop);
			return Task.CompletedTask;
		}
		finally {
			Engine.ReleaseLocalService(service, Guid.Empty);
		}
	}
}