0

I have a interface where I am returning result for a check and this interface has currently 2 implantations but in future it will keeps added.

public interface ICheck
{
    Task<CheckReturn> GetCheckReturn(string fileName);
}

public class ATypeCheck : ICheck
{
    public async Task<CheckReturn> GetCheckReturn(string fileName)
    {
        //logic to check A Type
        //return CheckReturn with right content type
        return await Task.FromResult<CheckReturn>(new CheckReturn { ContentType = "Type A" });
    }
}

public class BTypeCheck : ICheck
{
    public async Task<CheckReturn> GetCheckReturn(string fileName)
    {
        //logic to check A Type
        //return CheckReturn with right content type
        return await Task.FromResult<CheckReturn>(new CheckReturn { ContentType = "Type B" });
    }
}
// Future CTypeCheck
// Future DTypeCheck

With below code I am able to do only one check validation, but I need to check all the implementation of ICheck?

static async Task Main(string[] args)
    {
        ATypeCheck check = new ATypeCheck();

        var result = await check.GetCheckReturn("XYZ");
    }

Which design pattern help here & how?

0

2 Answers 2

1

2 Parts to a solution:

Part 1: Code against interfaces

static async Task Main(string[] args)
{
    ICheck checks = new List<ICheck>{ new ATypeCheck(), new BTypeCheck() };
    var results = new List<CheckResult>();
    foreach( var check in checks ) results.Add(await check.GetCheckReturn("XYZ"));

    // TODO handle results 
}

Part 2: Dependency Injection

In production code, you probably will want to detect all implementations of the interface at startup and inject your clients with that list of interface-implementations.

You can do that using reflection.

There are questions regarding this already, so I would consider this part a duplicate. You can refer to for example this question.

The reason I recommend doing this via DI and not just at runtime is that reflection might be slow and you would want that done once at startup and that's it. But you are not locked in to DI.

You could of course as well just construct the list of implementations like in the example of Part 1. But that would mean you'd have to keep track of new implementations and add them there (which is bad and will lead to "forgotten" impls sooner or later).

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you @Fildor. In the answer I can see only register part, but how to use it?
Do you have dependency injection set up in your actual app? If so, then you can add a constructor argument to a service that would use that list and DI will resolve it for you. Like ctor public MyService( ILogger<MyService> logger, IEnumerable<ICheck> typeChecks) { ... }
Ok. this this means I need to loop through IEnumerable<ICheck> typeChecks and as soon as one of the implantation satisfied the condition, I need to break the loop?
That's up to you. You can do that. You also could do the checks concurrently and await all to finish and then look at the results ...
1

If you're using DI you could register various implementations and then inject them as IEnumerable<YourInterface>

eg.

public class SomeConsumer() {
  private readonly IEnumerable<IYourInterface> _allServices;

  public SomeConsumer(IEnumerable<IYourInterface> allServices) {
    _allServices = allServices;
    // ...
  }
  
  // use _allServices in code to get all implementations
}

To register various implementations use standard AddScoped, AddTransient etc. methods.

// Program.cs
// ...
builder.Services.AddScoped<IYourInterface, Implementation1>();
builder.Services.AddScoped<IYourInterface, Implementation2>();
//...

4 Comments

Thanks ! This means I need to loop through IEnumerable<IYourInterface> allServices` and as soon as one of the implementation satisfied the condition, I need to break the loop?
The downside to this vs using reflection is again: you'd have to keep that list of registrations up-to-date with new implementations. I am not saying "this is bad". Just pointing out the need for maintainance later on. If that is acceptable, that's totally fine.
You could also use reflection to inject services, there's no need to manually register services one by one. Or use a library as Autofac to simplify registration of services.
To break the loop simply iterate over the IEnumerable with a foreach and use break as soon as you find a check (if you run in parallel use a CancellationToken).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.