variable
variable

Reputation: 9664

What is the difference between static class and singleton in .NET MVC project?

I understand difference between static class and singleton, importantly that singleton can be instantiated once where as static class doesn't need an instance.

This question is with the perspective of a .NET MVC project, to help me make decision between using either of them.

So assuming I have Class(es) with methods like the examples given below:

  1. I have a method like ConvertMeterToMiles(int mtr), where there is no dependency injected.

  2. Or a method like SendEmail(str eaddress), where there is no dependency injected but it instantiates new SMTPClient... followed by disposing the SMTPClient in the finally

Assuming I want to put the method into utility service class, then should I create a static class or singleton (ofcource with dependency injection)?

I understand there is no point of scoped or transient because there is no benefit to have new instances.

Upvotes: 6

Views: 3931

Answers (7)

Guru Stron
Guru Stron

Reputation: 142018

I would say that in context of your application the difference boils down to using DI or not and who is controlling lifetime/instantiation/injection.

Moving some functionality into some static helper class can be pretty fine if it should not differ based on environment or some other source of variability (another thing to consider - if the method is pure or not, i.e. pure functions usually can be good candidates too). For example ConvertMeterToMiles seems to be a fine candidate for such handling.

SendEmail on the other hand does not seem to be one - there can be environments where you don't want to send emails (test for example), or in future you anticipate having multiple implementations (or need to reimplement it) for this functionality (for example for some contexts email sending can be deferred using queue for example which will be handled by some background worker or another service). In this case you can highly leverage existence of DI and having encapsulated this functionality and hidden it behind contract (also I would say that handling SMTPClient settings is cleaner when they are registered in DI and resolved for encapsulated implementation).

Upvotes: 4

Yılmaz Durmaz
Yılmaz Durmaz

Reputation: 3014

Let me recap this first: a static class cannot have instances so you use its methods with the class's name and a singleton class can only have 1 instance to be shared by others.

for a static class, you need to include that in your code file. for a singleton class, you will first create an instance then pass it to other methods in their parameter list.

In the context of aspnet, since we will have only 1 instance, we leave its creation and disposal to the framework with services.AddSingleton<ISingleton, Singleton>(); and get it to our controller by public SomeController(ISingleton singleton). when visitors hit this controller endpoint, all of their requests will be different but then handled by this single instance. aspnet will determine which singletons your controller needs, by their interfaces, and inject only those requested.

whether be a server-side global state holder, a database connector, or an email-sender in your case, all activity you implement will go through this singleton instance. you can implement a load balancer into it so requests can be processed without bottlenecks.

on the other hand, for a static class, you will prefer short-lived methods as they will run separately for every request to the controller. distance converter is one such method. it won't require any long processes to do its job, nor it will depend on other expensive resources. however, you may want to cache most frequent calculations and send responses from the cache, then converting this distance converter into a singleton that uses resources for long times would be a better idea.

so in short, depending on the use of resources, you will prefer either short-lived independent methods or long-lived methods with lots of expensive operations.


seeing OP has a confusion about SMTPClient he uses, I wanted to add a few more lines.

you need to ask a question: does this client opens a channel to SMTP server and holds it for long time uses, or it just sends 1 message over it and needs be closed after.

some clients have one-time use core functionality, others build upon this core behavior and add a pool of pre-open single-use connections. the core functional class can be used both as a static, given resources as parameters, or be a singleton if it allows having initialized resources other than the connection itself. both cases will need to open a channel to SMTP server only when they are used, and that will cause delays. lastly, if it has to be closed after use, then the core functionality cannot be used as a singleton, as we need the alive throughout the life of our service.

on the other hand, if the client uses a pool of connections, there is no argue that it will be a singleton and positively affect the user experience. a side note here will be implementing one's own class having this pool of connections if no other current library is available in project's use environment.

Upvotes: 1

Ladrillo
Ladrillo

Reputation: 89

There's not difference of funcionality between Singleton and Static.

In a threaded environment like you describe, the only thing that would be a problem is sharing data. If multiple threads access the same properties, there WILL be concurrency problems.

Now, if you are just using utility methods like Sum(int a, int b) which don't have any state, there wont be any problems.

Now, there's basically no difference in this situation between the two, other than the singleton needing to be injected. Even related to web api, there isn't anything really special.

Except maybe that a singleton class can inherit, and a static class cant. But that's another topic.

Upvotes: 0

Anor
Anor

Reputation: 76

As you said singleton can be instantiated once, so it's a good place to keep object alive, object you can use during application lifetime. For mvc project, singleton objects are the same for every request.

In your method 2, your SmtpClient doesn't need to create a new instance and dispose it every time.

From msdn doc :

The SmtpClient class implementation pools SMTP connections so that it can avoid the overhead of re-establishing a connection for every message to the same server. An application may re-use the same SmtpClient object to send many different emails to the same SMTP server and to many different SMTP servers. As a result, there is no way to determine when an application is finished using the SmtpClient object and it should be cleaned up.

So it's a good candidate for singleton utility service.

The singleton pattern will look like this :

public class SmtpUtilityService : ISmtpUtilityService, IDisposable
{
    private readonly SmtpClient _smtpClient;
    
    public SmtpUtilityService()
    {
        _smtpClient = new SmtpClient([...]);
    }
    
    public async Task SendEmail(str eaddress)
    {
        await _smtpClient.SendAsync([...]);
    }

    public void Dispose()
    {
        if(_smtpClient != null)
        {
            _smtpClient.Dispose();
        }
    }
}

In your Statup.cs add SmtpUtilityService as singleton to the IServiceCollection and your SmtpClient will be instantiated only once.

By the way microsoft doesn't recommend the use of SmtpClient (obsolete on some platforms and not recommended on others) so not sure if it's a good candidate :/

msdn smtpclient obsolete

And for your first method ConvertMeterToMiles(int mtr), it's just tranformation, one calcul at one time. it doesn't need any properties and it doesn't need an instance. So full static class is good choice.

public static class MeterHelper
{
    public static decimal ConvertMeterToMiles(int mtr)
    {
        return mtr * 0.0006213712;
    }
}

Personally I don't often use singleton. If I need properties I will use scoped or transient services and if I don't I will use full static class (helpers).

Upvotes: 2

DaveF
DaveF

Reputation: 419

I would recommend using an injected singleton. Functionally, it makes very little difference, but injecting a singleton has big advantages when it comes to testing.

Whilst static methods themselves are easy to test, it becomes very difficult to test the code that uses them independently.

Let's take your SendEmail(str eaddress) example. If we implement this as a static helper method, it will be impossible to unit test the code which uses this method without creating a real SMTPClient. By contrast, if we inject a singleton helper class with an interface, this interface can be mocked when testing the code which calls SendEmail.

Upvotes: 0

Gabriel Luci
Gabriel Luci

Reputation: 40928

A static class is a singleton. The only difference between creating a singleton as a static field or via dependency injection is how it's accessed, that's all.

But in the context of SmtpClient, it's not thread-safe. In fact, the documentation says:

Note

If there is an email transmission in progress and you call SendAsync or Send again, you will receive an InvalidOperationException.

In other words, you can't send two emails at the same time using the same instance of SmtpClient. So using SmtpClient as any kind of singleton isn't a good idea anyway. You're better off either making it scoped, or don't use DI for it at all and just declare a new one when you need it.

Upvotes: 1

Pierre Plourde
Pierre Plourde

Reputation: 1042

Adding a singleton via dependency injection instantiates an instance of the requested class. A static class cannot be instantiated, and so you simply access its methods from elsewhere, based on the static class's access modifiers.

Upvotes: 4

Related Questions