Memory leak in Xamarin Forms app when using DI in a Task - c#

I am creating a Xamarin Forms application, and I am using the Xamarin Profiler to show that I have a memory leak. I have tracked the memory leak down to where it is happening, but I can't understand WHY it is happening.
I have a class (we will call it MyClass for now). And that class is using a Timer to call a service once every second. That service makes a REST call to retrieve a bunch of information, and then serializes the results back into an object....
MyClass:
public class MyClass : ContentPage
{
private readonly IMyService myService;
public MyClass() : base()
{
}
protected override async void OnAppearing()
{
StartTimer();
}
private void StartTimer()
{
Task.Run(async() =>
{
while(true)
{
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
//--- everytime I call myService.GetSystemStatus(), my allocated memory continues to rise
MyResponse response = await myService.GetSystemStatus();
Device.BeginInvokeOnMainThread(() =>
{
// update the UI here...
});
await Task.Delay(1000);
}
});
}
}
MyService (Singleton):
public class MyService : IMyService
{
private readonly IMyHttpClientFactory httpClientFactory;
public MyService(IMyHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public async Task<MyResponse> GetSystemStatus()
{
return await httpClientFactory.Create().GetAsync<MyResponse>(
"http://example.com/api/status"
);
}
}
MyHttpClientFactory (Singleton):
public class MyHttpClientFactory : IMyHttpClientFactory
{
private readonly IServiceProvider _serviceProvider;
public MyHttpClientFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public MyHttpClient Create()
{
return _serviceProvider.GetRequiredService<MyHttpClient>();
}
}
MyHttpClient:
public class MyHttpClient : IDisposable
{
private HttpClient _httpClient;
public MyHttpClient ()
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(10);
}
public async Task<T> GetAsync<T>(string url) where T : new()
{
string s = await GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(s);
}
public async Task<string> GetStringAsync(string url)
{
using (var response = await _httpClient.GetAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
My services are defined as follows:
public partial class App : Application
public ServiceProvider serviceProvider;
public App()
{
IServiceCollection services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
InitializeComponent();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<MyHttpClient>("MyHttpClient", x =>
{
x.Timeout = TimeSpan.FromSeconds(5);
});
services.AddSingleton<IMyHttpClientFactory, MyHttpClientFactory>();
services.AddSingleton<IMyService, MyService>();
}
}
Best I can tell, the memory is going up because I am referencing the DI MyService inside a separate thread. But I am not sure if this is the reason or if there is something else that would be causing the leak?
Any advice would be greatly appreciated!!!
Thanks!

From what I understand from your code and your comments, it looks like you're looping by calling StartTimer() inside the Device.StartTimer() method.
According to the documentation, Device.StartTimer() is recurring and will occur every X seconds, depending of your interval parameter.
By removing the call to StartTimer() (the one between t.Dispose() and return false of MyClass.StartTimer, your code should work as expected and you will not create a new timer every x seconds

What could be the cause of the leak:
Your MyHttpClient class implements the IDisposable interface, yet the code to use an instance of this class is not leveraging the disposable nature of the object.
Even though the internal HttpClient instance is wrapped in a using statement, the MyHttpClient instance will not be disposed of as you would expect.
// from MyHttpClient class
public async Task<MyResponse> GetSystemStatus()
{
// no using statement here
return await httpClientFactory.Create().GetAsync<MyResponse>(
"http://example.com/api/status"
);
}
// should be:
public async Task<MyResponse> GetSystemStatus()
{
using (var client = await httpClientFactory.Create())
{
return await client.GetAsync<MyResponse>("http://example.com/api/status");
}
}
Another thing to try is to change the location of the resolution of the MyService instance to inside the Task since this is where it is used. This will allow the task to own the resource, and allow it to be collected when the task is complete.
private void StartTimer()
{
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
Task t = Task.Run(async() =>
{
// resolve the service here
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
MyResponse response = await myService.GetSystemStatus();
Device.BeginInvokeOnMainThread(() =>
{
// update the UI here...
});
});
t.Wait();
t.Dispose();
StartTimer();
return false;
});
}
A couple of additional observations of your code:
In your HttpClientFactory's Create() method, you are resolving an instance of your client from the DI container.
Your MyHttpClient class has a default constructor which means the resolution is not needed since there are no additional dependencies requiring DI support.
Your code could simply return a new MyHttpClient() instance from the Create() method without the need for DI.
Your MyHttpClient also implements the IMyHttpClient interface, but your factory returns the concrete type. This means you need to either remove the interface as unnecessary or change the return type to be the interface type since the interface is redundant unless it is used.

Thank you all for your answers....
I finally figured out the source of the memory leak.
The problem was that I was referencing "MyService" like this:
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
The problem was that the serviceProvider object was a public property on my App. So each time I referenced the provider inside my loop, it was creating the leak.
To get around this, I added an abstract method to each of my pages that implemented MyClass to return the service correctly using DI. This has corrected my memory leak issue....
Thanks all for the help!

I don't think that your timer logic is the cause of the leak.
But in case it is useful to you, here is a clean way to do work periodically, yet if work takes a long time, avoid events "piling up".
Given await/async, no Timer is needed.
(There is an alternative solution that starts/stops a single System.Timers.Timer, but I won't go into that here.)
Replace StartTimer() declaration with the following:
/// <summary> Runs until keepRunning() returns false.
/// Delays by "msecDelay" AFTER finishing the previous loop's non-UI work. </summary>
private void StartTaskLoopWhileKeepRunning(Func<bool> keepRunning, int msecDelay = 250)
{
Task.Run(async () =>
{
while (keepRunning())
{
// Do non-UI work here.
// ... possibly slow work ...
Device.BeginInvokeOnMainThread(() =>
{
// NOTE: This work will run in parallel with the next delay.
// ... Do UI work here. ...
});
// Non-UI thread sleeps for msec.
await Task.Delay(msecDelay);
}
});
}

Related

Async load data in BaseController, how to make controller constructor async

I run .net core 6 version, and I have no idea how to make constructor of my controller (BaseController) async, because i am calling async service to load items in Menu. Menu is on every page, so any other of my controllers (AccountController, OrderController) inherits from my BaseController.
It must be loaded in constructor, just on creating the controller, I cant hang on it on another action. Right now, when i just make it sync, on first page call there are no preloaded data, because it's not waiting on completition.
public class BaseController : Controller
{
private readonly ICategoryService _categoryService;
protected readonly IMapper _mapper;
protected LayoutViewModel _layoutViewModel = new LayoutViewModel();
public BaseController(ICategoryService categoryService, IMapper mapper)
{
_categoryService = categoryService;
_mapper = mapper;
LoadRankedCategories();
}
public void LoadCategories()
{
var categories = _categoryService.GetCategoriesAsync();
_layoutViewModel.Categories = _mapper.Map<IEnumerable<MenuCategoryViewModel>>(categories);
}
}
Ok, on controller construction start the asynchronous task and save said task in a field. Then make sure you await said task whenever you need the results.
public class BaseController : Controller
{
private Task _categoriesTask;
public BaseController()
{
_categoriesTask = LoadCategoriesAsync();
}
private async Task LoadCategoriesAsync()
{
var rawCategories = await _categoryService.GetCategoriesAsync();
return _mapper.Map<IEnumerable<MenuCategoryViewModel>>(rawCategories);
}
[HttpGet]
public async Task<IActionResult> SomeGet()
{
// Await the class-level task.
var categories = await _categoriesTask;
...
}
}
NOTE: As I see the sample here, however, you should do this in the static constructor or use some caching mechanism like memcached (if microservices or distributed) or Redis, assuming the categories never change. So take the solution as a purely academic response on how to overcome the problem. In reality, this doesn't feel right for the stated reason.
Finally, I wouldn't recommend this at all unless there is absolutely no other way: You can spawn a new thread and block it with Result. This might carry undesired issues, so use it at your own risk.
public BaseController()
{
// This will get you the actual categories.
_categories = Task.Run(() => LoadCategoriesAsync()).Result();
}

Delaying a task in C# - db context disposed

I have a situation where I need certain code to execute at a certain time (in my ASP .NET Core project).
I know that delaying a task is not a great solution, but this is what I have and I'd like to know how to make it work:
async Task MyMethod()
{
// do something
// Create a new thread that waits for the appropriate time
TimeSpan time = dbAppointment.ScheduledOn - TimeSpan.FromMinutes(5.0) - DateTime.UtcNow;
_ = Task.Delay(time).ContinueWith(async x => await
notificationManager.CreateReminder());
// continue doing something
}
When I try to run the code, it enters the method that is supposed to be executed, at the right time:
public async Task CreateReminder() {}
but fails when it tries to use my dbContext which I injected using DI into the NotificationManager constructor, stating that it was disposed.
This is the "flow" of the dependencies:
public class MyClass
{
private readonly MyContext dbContext;
private readonly INotificationManager notificationManager;
public MyClass(MyContext context, INotificationManager nm)
{
dbContext = context;
notificationManager = nm;
}
public async Task MyMethod() // the method shown in the snippet above
{
// method does something using the dbContext
_ = Task.Delay(time).ContinueWith(async x => await
notificationManager.CreateReminder());
}
}
public class NotificationManager: INotificationManager
{
private readonly MyContext dbContext;
public NotificationManager(MyContext context) { dbContext = context;}
public async Task CreateReminder() { // this method uses the dbContext}
}
DI setup in startup.cs:
services.AddDbContext<MyContext>();
services.AddScoped<INotificationManager, NotificationManager>();
Options
Use a job scheduler (Like Hangfire, Quartz.Net, Jobbr, ...)
Use a background service if your .net core version is >= 2
In both cases you'll need to inject the DatabaseContext in the job class otherwise you'll receive an ObjectDisposedException.
When you need to scale-out to multiple machines you'll need a job server with a state store like SQL Server, MSMQ, RabbitMQ, Redis,...
Sample with Hangfire
public class MyDelayJob
{
private readonly MyContext dbContext;
private readonly INotificationManager notificationManager;
public MyDelayJob(MyContext context, INotificationManager nm)
{
dbContext= context;
notificationManager = nm;
}
public async Task Run(/*parameters*/)
{
await notificationManager.CreateReminder()
}
}
/*Shedule code in MyMethod
IBackgroundJobClient can be injected
you need to register MyDelayJob with your IOC container.
*/
backgroundJobClient.Schedule<MyDelayJob>(j => j.Run(), TimeSpan.FromSeconds(60))
See the docs for IBackgroundJobClient

Can I use an AutFac factory to create my DbContext

I am trying to implement an auto-refresh using MemoryCache by specifying a CacheEntryUpdateCallback delegate that is called when the cached item expires.
The delegate calls a method in my repository:
public async Task<List<Foo>> GetFoos()
{
return await _dbContext.Foos.ToListAsync();
}
That throws an exception in the callback because the context has already been disposed (the original HttpRequest has long since returned)
So I tried using an Autofac factory to inject my dependency instead:
public FooRepository(Func<<IFooContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<List<Foo>> GetFoos()
{
return await _dbContextFactory().Foos.ToListAsync();
}
That gave me a different exception:
Instances cannot be resolved and nested lifetimes cannot be created
from this LifetimeScope as it has already been disposed.
What about this "Owned" factory thing?
public FooRepository(Func<Owned<IFooContext>> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<List<Foo>> GetFoos()
{
using(var factory = _dbContextFactory())
{
return await factory.Value.Foos.ToListAsync();
}
}
Nope, same problem:
Instances cannot be resolved and nested lifetimes cannot be created
from this LifetimeScope as it has already been disposed.
What can I do to get around this problem?
you should have hosted service for long run process and kind of refresh queue to feed it
with hosted service you can get DbContext in temporary scope as follow
public class TimedHostedService : IHostedService
{
private readonly IServiceScopeFactory scopeFactory;
public TimedHostedService(IServiceScopeFactory scopeFactory)
{
this.scopeFactory = scopeFactory;
}
private void DoWork()
{
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
}
}
}
about hosted service

FromResult or IServiceScopeFactory in Blazor server-side

So I've been injecting IServiceScopeFactory to my pages to use it's scope when getting my data through EF Core.
But yesterday I stumbled upon somebody using Task.FromResult when calling the database.
Is one preferred over the other and if so why?
Thank you in advance!
Ex Task.FromResult
//In code behind
[Inject]
IMyService myService { get; set; }
protected void GetSomeData()
{
var someData = await myServie.GetSomeData();
}
//From serviceClass
public async Task<List<SomeData>> GetSomeData(int id)
{
return await Task.FromResult(db.SomeTable.Where(x => x.Id == qualifierVersionId).AsEnumerable());
}
Ex
//In code behind
[Inject]
IServiceScopeFactory ScopeFactory { get; set; }
protected void GetSomeData()
{
using (var serviceScope = ScopeFactory.CreateScope())
{
var myService = serviceScope.ServiceProvider.GetService<IMyService>();
var someData = await myServie.GetSomeData();
}
}
//From serviceClass
public async Task<List<SomeData>> GetSomeData(int id)
{
return await db.SomeTable.Where(x => x.Id == id).ToListAsync();
}
Edit (because the questions of why I want to know which way to go)
I need to use one or the other because when using Server side Blazor the lifespan makes a Scoop behave like a Singleton. So if I for instance call return await db.SomeTable.Where(x => x.Id == id).ToListAsync(); without the IServiceScopeFactory it will live until you close the website. So this creates an error: 'A second operation started on this context before a previous operation completed.'.
You can't compare the two as they don't have anything in common.
Task.FromResult just creates a task that's completed successfully with the specified result. I believe one of the main use cases for using Task.FromResult is when you're implementing an async interface but your implementation is synchronous, as in the example above. This is not really a good idea on DB calls as they could take a long time.
I'm not sure why you're using the IServiceScopeFactory above. If it's so that the service you're requesting is scoped to the lifetime of the component you're requesting it in then there is a better way.
public class MyComponent : OwningComponentBase<IMyService>
{
protected void GetSomeData(int id)
{
var someData = await Service.GetSomeData(id);
}
}
public class MyService : IMyService
{
public async Task<List<SomeData>> GetSomeData(int id)
{
return await db.SomeTable.Where(x => x.Id == id).ToListAsync();
}
}
By using OwningComponentBase a service scope is created for you under the hood and the service is exposed by the base component via the Service property. I've written a blog post on it if you want to know more.

How to call some async code in an ASP.NET application_start

In our application_startup, we seed up our database with some fake data, if no data exists.
To do this, we're using the Async methods to store the data. Great. Only problem is, we're not sure how to do this in the application_startup because that's not an async method.
I've spent soooo much time trying to understand #StevenCleary's tutorials and I'm always getting deadlocks. I totally grok what he consistently says:
As a general rule, you should use "async all the way down"; that is, don't block on async code
but I just don't get how I can do that, in this case :(
Lets imagine this is the code I'm trying to play with...
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStore = new DocumentStore();
await documentStore.InitializeAsync(someFakeData);
...
// Registers this database as a singleton.
Container.Register(documentStore);
}
and later on .. some code that uses this documentStore. It is injected via construction injection ...
public SomeController(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
public ViewModel GetFoos()
{
using (var session = _documentStore.OpenSession())
{
... db code goes in here ...
}
}
Clarification
I'm not trying to do some async code in here. I'm actually trying to call this async method, synchronously. Sure, i loose the benefits of async blah blah de blah.. but i'm happy with that. This is start up and I'm happy to block on startup.
In this case, you're asynchronously initializing a shared resource. So, I recommend that you either save the Task itself, or introduce an asynchronous wrapper type.
Using Task:
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStore = new DocumentStore();
var documentStoreTask = documentStore.InitializeAsync(someFakeData);
...
// Registers this database task as a singleton.
Container.Register(documentStoreTask);
}
That may be too awkward, though, depending on Container. In that case, you can introduce an asynchronous wrapper type:
public sealed class DocumentStoreWrapper
{
private readonly Task<DocumentStore> _documentStore;
public DocumentStoreWrapper(Data data)
{
_documentStore = CreateDocumentStoreAsync(data);
}
private static async Task<DocumentStore> CreateDocumentStoreAsync(Data data)
{
var result = new DocumentStore();
await documentStore.InitializeAsync(data);
...
return result;
}
public Task<DocumentStore> DocumentStoreTask { get { return _documentStore; } }
}
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStoreWrapper = new DocumentStoreWrapper(someFakeData);
...
// Registers this database wrapper as a singleton.
Container.Register(documentStoreWrapper);
}
Or, you could use AsyncLazy<T>, which does much the same thing but uses a background thread to execute the initialization code.
You can use of Task.Run(() => YourAsyncMethod()); inside of none async method like:
protected void Application_Start()
{
Task.Run(() => MyAsyncMethod(true));
}
This is an old topic, but it's popped up in my search and maybe it will for others.
For what the OP has requested (ie. To run an async method in a synchronous way from inside a synchronous method, and block until it's finished), is there some reason that the use of Task.WaitAll would not be a simple and adequate way of addressing this?
protected void Application_Start()
{
Task.WaitAll(MyAsyncMethod(true));
}
public static class AsyncHelper
{
private static readonly TaskFactory MyTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return MyTaskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
MyTaskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
then use as
AsyncHelper.RunSync(ProcessAsync);
private async Task ProcessAsync(){ ....

Categories