2
votes

I'm trying to read data from the database every second using the following code:

public class DBChangeService : DelegatingHandler, IHostedService
{
    private Timer _timer;
    public SqlDependencyEx _sqlDependency;
    private readonly IHubContext<SignalServer> _hubcontext;
    private readonly IServiceScopeFactory _scopeFactory;
    string connectionString = "";
    public IConfiguration Configuration { get; }

    public DBChangeService(IConfiguration configuration, IHubContext<SignalServer> hubcontext, IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
        _hubcontext = hubcontext;
        Configuration = configuration;
        connectionString = Configuration.GetConnectionString("DefaultConnection");
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(Heartbeat, null, 0, 1000);
        return Task.CompletedTask;
    }


    public void Heartbeat(object state)
    {
         _hubcontext.Clients.All.SendAsync("SBSystemBrodcasting", SBSystemApps());

    }

    public async Task<List<SomeModel>> SBSystemApps()
    {
        var data = new List<SomeModel>();

        using (var scope = _scopeFactory.CreateScope())
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                string commandText = @"SELECT 
                        a.name as AppName,
                        a.state as AppState,
                        a.created_at as AppCreatedAt,
                        a.updated_at as AppUpdatedAt,
                        a.foundation as AppFoundation,

                        s.name as SpaceName,
                        o.name as OrgName
                    FROM
                        apps as a
                    INNER JOIN
                        spaces as s ON a.space_guid = s.space_guid
                    INNER JOIN
                        organizations as o ON s.org_guid = o.org_guid
                    where s.name = 'system' and o.name = 'system' and a.foundation = 2";

                try
                {
                    SqlCommand cmd = new SqlCommand(commandText, connection);

                    await connection.OpenAsync();
                    using (DbDataReader reader = await cmd.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            var sqlresult = new SomeModel
                            {
                                AppName = reader["AppName"].ToString(),
                                AppState = reader["AppState"].ToString(),
                                AppCreatedAt = Convert.ToDateTime(reader["AppCreatedAt"]),
                                AppUpdatedAt = Convert.ToDateTime(reader["AppUpdatedAt"]),
                                AppFoundation = Convert.ToInt32(reader["AppFoundation"]),
                                SpaceName = reader["SpaceName"].ToString(),
                                OrgName = reader["OrgName"].ToString(),
                            };

                            data.Add(sqlresult);
                        }

                    }
                }
                finally
                {
                    connection.Close();
                }

                return data;
            }
        }
    }


    public Task StopAsync(CancellationToken cancellationToken)
    {
        //Timer does not have a stop. 
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }
}

getting following error:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'task' with type 'System.Runtime.CompilerServices.AsyncTaskMethodBuilder1+AsyncStateMachineBox1[System.Collections.Generic.List`1[TestApp.Models.SomeModel],TestApp.Services.DBChangeService+d__11]'.

I have tried almost all possible solutions I could find on StackOverflow but none are working

In startup:

services.AddMvc().AddJsonOptions(options =>
            {
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            });

On the header of my class, I tried using [JsonObject(IsReference = true)] but none are working for me any thoughts?

Model:

[JsonObject(IsReference = true)]
    public partial class SomeModel
    {
        public string AppName { get; set; }
        public string AppState { get; set; }
        public DateTime AppCreatedAt { get; set; }
        public DateTime AppUpdatedAt { get; set; }
        public int AppFoundation { get; set; }
        public string OrgName { get; set; }
        public string SpaceName { get; set; }
    }
1
If JSON.Net is throwing the error (and that specific one) then it's likely your model(s)and has 0 to do with the rest. My guess without seeing it is you have a circular reference between two classes that is giving it a headache. Remove the irrelevant code and post the model(s).gilliduck

1 Answers

3
votes
_hubcontext.Clients.All.SendAsync("SBSystemBrodcasting", SBSystemApps());

The issue is that you are calling SendAsync with a Task. This is not how the API is designed to be used. You should be passing through the actual payload you want to use.

There are multiple ways to solve this. One would be to use:

_hubcontext.Clients.All.SendAsync("SBSystemBrodcasting", SBSystemApps().GetAwaiter().GetResult());

although I would recommend reading this guidance about why it isn't a great idea.

Another would be to change SBSystemApps so it is synchronous (i.e. returns List<T> rather than Task<List<T>>.