Use Interface to resolve MediatR IRequestHandler instead of the class - c#

I have implement MediatR in .net 5 application and want to resolve require dependencies using Handler interfaces. Currently I using class name to resolve as following
_mediator.Send(new GetDeviceByIMEI(imei)); // want to use interface ??
//var result = await _mediator.Send(IGetHandHeldByIMEI????);
full code reference as following;
Handler Interface
public interface IGetDeviceByIMEIEventHandler
{
Task<DeviceWrapperDataView> Handle(GetDeviceByIMEI request, CancellationToken cancellationToken);
}
Query Interface
public interface IGetDeviceByIMEI
{
string IMEI { get; set; }
}
Query
public class GetDeviceByIMEI: IRequest<DeviceWrapperDataView>
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
Handler
public class GetDeviceByIMEIEventHandler : IRequestHandler<GetDeviceByIMEI, DeviceWrapperDataView>, IGetDeviceByIMEIEventHandler
{
private readonly IDeviceEntity _DeviceEntity;
public GetDeviceByIMEIEventHandler(IDeviceEntity DeviceEntity)
{
_DeviceEntity = DeviceEntity;
}
public async Task<DeviceWrapperDataView> Handle(GetDeviceByIMEI request, CancellationToken cancellationToken)
{
// code to get data
return DeviceOutput;
}
}
API controller
private readonly IMediator _mediator;
public DeviceController(
IMediator mediator)
{
_mediator = mediator;
}
[HttpGet()]
public async Task<IActionResult> GetDeviceByIMEI(string imei)
{
Var result = await _mediator.Send(new GetDeviceByIMEI(imei));
// want to use
}

To do that you have to register your handler in the container with each of the class that inherit your query interface.
For instance with the code you provided.
public interface IGetDeviceByIMEI : IRequest<DeviceWrapperDataView>
{
string IMEI { get; set; }
}
public class GetDeviceByIMEI: IGetDeviceByIMEI
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
public class AnotherGetDeviceByIMEI: IGetDeviceByIMEI
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
public class GetDeviceByIMEIEventHandler : IRequestHandler<IGetDeviceByIMEI, DeviceWrapperDataView>, IGetDeviceByIMEIEventHandler
{
private readonly IDeviceEntity _DeviceEntity;
public GetDeviceByIMEIEventHandler(IDeviceEntity DeviceEntity)
{
_DeviceEntity = DeviceEntity;
}
public async Task<DeviceWrapperDataView> Handle(IGetDeviceByIMEI request, CancellationToken cancellationToken)
{
// code to get data
return DeviceOutput;
}
}
Once you have done that, you have to register the handler in your container with each use case.
For instance in .Net Core, you can do it with the serviceCollection in the StartUp class.
serviceCollection.AddTransient<IRequestHandler<GetDeviceByIMEI, DeviceWrapperDataView>, GetDeviceByIMEIEventHandler >();
serviceCollection.AddTransient<IRequestHandler<AnotherGetDeviceByIMEI, DeviceWrapperDataView>, GetDeviceByIMEIEventHandler >();
Regards.

Related

EF Core: Implement a single endpoint for all Subtypes

I'm having an issue where I try to make one endpoint for all classes that derive the same class.
One of my Core Entities is called Cell and has many deriving types such as ImageCell, VideoCell and so on.
The project is implemented using Ardalis.Specification and Ardalis.Specification.EntityFrameworkCore.
For reference here is the base class Cell and two deriving classes.
public abstract class Cell : IAggregateRoot
namespace Core.Entities.Aggregates
{
public abstract class Cell : IAggregateRoot
{
public int CellId { get; set; }
public string CellType { get; set; }
public int RowIndex { get; set; }
public int ColIndex { get; set; }
public int RowSpan { get; set; }
public int ColSpan { get; set; }
public int PageId { get; set; }
public Page Page { get; set; }
}
}
namespace Core.Entities.Cells
{
public class ImageCell : Cell
{
public string Url { get; set; }
}
}
namespace Core.Entities.Cells
{
public class TextCell : Cell
{
public string Text { get; set; }
}
}
All classes have a corresponding DTO.
namespace API.DTOs
{
public class CellDTO : DTO
{
public int CellId { get; set; }
public string CellType { get; set; }
public int RowIndex { get; set; }
public int ColIndex { get; set; }
public int RowSpan { get; set; }
public int ColSpan { get; set; }
public int PageId { get; set; }
}
}
namespace API.DTOs.Cells
{
public class ImageCellDTO : CellDTO
{
public string ImageUrl { get; set; }
}
}
namespace API.DTOs.Cells
{
public class TextCellDTO : CellDTO
{
public string Text { get; set; }
}
}
The MappingProfile is set up according to the documentation:
namespace API
{
public class MappingProfile : Profile
{
public MappingProfile()
{
// Entity -> DTO
...
// Cells
// https://docs.automapper.org/en/stable/Mapping-inheritance.html
CreateMap<Cell, CellDTO>()
.IncludeAllDerived();
CreateMap<ImageCell, ImageCellDTO>();
CreateMap<AudioTextCell, AudioTextCellDTO>();
CreateMap<AudioCell, AudioCellDTO>();
CreateMap<GameCell, GameCellDTO>();
CreateMap<TextCell, TextCellDTO>();
CreateMap<VideoCell, VideoCellDTO>();
...
// DTO -> Enitity
...
// Cells
CreateMap<CellDTO, Cell>()
.IncludeAllDerived();
CreateMap<AudioTextCellDTO, AudioTextCell>();
CreateMap<AudioCellDTO, AudioCell>();
CreateMap<GameCellDTO, GameCell>();
CreateMap<TextCellDTO, TextCell>();
CreateMap<VideoCellDTO, VideoCell>();
CreateMap<ImageCellDTO, ImageCell>();
...
}
}
}
The Repository is set up like this:
using Ardalis.Specification;
namespace Core.Interfaces
{
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
{
}
}
using Ardalis.Specification;
namespace Core.Interfaces
{
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
{
}
}
namespace Infrastructure.Data
{
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
public EfRepository(BookDesinerContext dbContext) : base(dbContext)
{
}
}
}
Service like this:
namespace Core.Interfaces
{
public interface IService<T> where T : class, IAggregateRoot
{
Task<bool> ExistsByIdAsync(int id);
Task<T> GetByIdAsync(int id);
Task<T> GetByIdAsyncWithSpec(Specification<T> spec);
Task<IEnumerable<T>> ListAsync();
Task<IEnumerable<T>> ListAsyncWithSpec(Specification<T> spec);
Task DeleteByIdAsync(int id);
Task DeleteRangeAsync(IEnumerable<T> range);
Task<T> AddAsync(T t);
Task UpdateAsyc(T t);
}
}
Now I created a default implementation:
using Ardalis.Specification;
using Core.Interfaces;
namespace Core.Services
{
public class GenericService<T> : IService<T> where T : class, IAggregateRoot
{
private readonly IRepository<T> _repository;
private readonly IAppLogger<GenericService<T>> _logger;
public GenericService(IRepository<T> repository, IAppLogger<GenericService<T>> logger)
{
_repository = repository;
_logger = logger;
}
public async Task<bool> ExistsByIdAsync(int id)
{
return await _repository.GetByIdAsync(id) != null;
}
public async Task<T> GetByIdAsync(int id)
{
var t = await _repository.GetByIdAsync(id);
if (t == null)
{
_logger.Error($"Element with id: {id} can not be found!");
throw new ArgumentException($"Element with id: {id} can not be found!");
}
return t;
}
public async Task<T> GetByIdAsyncWithSpec(Specification<T> spec)
{
if (!(spec is ISingleResultSpecification))
{
throw new ArgumentException("Specification does not implement marker interface.");
}
ISingleResultSpecification<T> specification = (ISingleResultSpecification<T>)spec;
var t = await _repository.GetBySpecAsync(specification);
if (t == null)
{
_logger.Error($"Element can not be found!");
throw new ArgumentException($"Element can not be found!");
}
return t;
}
public async Task<IEnumerable<T>> ListAsync()
{
return await _repository.ListAsync();
}
public async Task<IEnumerable<T>> ListAsyncWithSpec(Specification<T> spec)
{
return await _repository.ListAsync(spec);
}
public async Task DeleteByIdAsync(int id)
{
var t = await _repository.GetByIdAsync(id);
if (t == null)
{
_logger.Error($"Element with id: {id} can not be found!");
throw new ArgumentException($"Element with id: {id} can not be found!");
}
await _repository.DeleteAsync(t);
}
public async Task DeleteRangeAsync(IEnumerable<T> range)
{
await _repository.DeleteRangeAsync(range);
}
public async Task<T> AddAsync(T t)
{
return await _repository.AddAsync(t);
}
public async Task UpdateAsyc(T t)
{
await _repository.UpdateAsync(t);
}
}
}
I registered a Service for every single Subtype:
builder.Services.AddScoped<IService<Cell>, GenericService<Cell>>();
builder.Services.AddScoped<IService<ImageCell>, GenericService<ImageCell>>();
builder.Services.AddScoped<IService<TextCell>, GenericService<TextCell>>();
builder.Services.AddScoped<IService<AudioCell>, GenericService<AudioCell>>();
builder.Services.AddScoped<IService<AudioTextCell>, GenericService<AudioTextCell>>();
builder.Services.AddScoped<IService<VideoCell>, GenericService<VideoCell>>();
builder.Services.AddScoped<IService<GameCell>, GenericService<GameCell>>();
And for the final part the controller:
namespace API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CellsController : BaseController<Cell, CellDTO>
{
private readonly IService<ImageCell> _imageCellService;
private readonly IService<TextCell> _textCellService;
private readonly IService<AudioCell> _audioCellService;
private readonly IService<AudioTextCell> _audioTextCellService;
private readonly IService<VideoCell> _videoCellService;
private readonly IService<GameCell> _gameCellService;
public CellsController(
IService<Cell> service,
IService<ImageCell> imageCellService,
IService<TextCell> textCellService,
IService<AudioCell> audioCellService,
IService<AudioTextCell> audioTextCellService,
IService<VideoCell> videoCellService,
IService<GameCell> gameCellService,
IMapper mapper) : base(service, mapper)
{
_imageCellService = imageCellService;
_textCellService = textCellService;
_audioCellService = audioCellService;
_audioTextCellService = audioTextCellService;
_videoCellService = videoCellService;
_gameCellService = gameCellService;
}
[HttpGet]
public override async Task<IActionResult> Get()
{
var result = new List<Object>();
// Add ImageCells
ICollection<ImageCell> imageCells = (ICollection<ImageCell>)await _imageCellService.ListAsync();
result.AddRange(_mapper.Map<ICollection<ImageCell>, ICollection<CellDTO>>(imageCells));
// Add TextCells
ICollection<TextCell> textCells = (ICollection<TextCell>)await _textCellService.ListAsync();
result.AddRange(_mapper.Map<ICollection<TextCell>, ICollection<CellDTO>>(textCells));
...
return Ok(result);
}
[HttpGet("Page/{pageId}")]
public async Task<IActionResult> GetByPageId(int pageId)
{
var result = new List<Object>();
// Add ImageCells
ICollection<ImageCell> imageCells = (ICollection<ImageCell>)await _imageCellService.ListAsync();
result.AddRange(_mapper.Map<ICollection<ImageCell>, ICollection<ImageCellDTO>>(imageCells.Where(c => c.PageId == pageId).ToList()));
// Add TextCells
ICollection<TextCell> textCells = (ICollection<TextCell>)await _textCellService.ListAsync();
result.AddRange(_mapper.Map<ICollection<TextCell>, ICollection<TextCellDTO>>(textCells.Where(c => c.PageId == pageId).ToList()));
...
return Ok(result);
}
[HttpGet("{id}")]
public override async Task<IActionResult> Get(int id)
{
if (await _imageCellService.ExistsByIdAsync(id))
{
var result = await _imageCellService.GetByIdAsync(id);
return Ok(_mapper.Map<ImageCell, ImageCellDTO>(result));
}
if (await _textCellService.ExistsByIdAsync(id))
{
var result = await _textCellService.GetByIdAsync(id);
return Ok(_mapper.Map<TextCell, TextCellDTO>(result));
}
...
return NotFound();
}
...
}
}
This is a highly inefficient implementation to my understanding.
Problems:
I can call /Cells to get all Cells the way it was intended with the List<Object>. List<CellDTO> always led to a downcast, which was unintended.
The same problem occures in a DTO that is not shown, that has a List<CellDTO> as a property. But I would need the concrete subtypes in this list.
My goals:
Remove redundant code in the controller
Only register one CellSerivce
Correct mapping Entity <=> DTO
Things I have considered, but I could not find information to back my thesis:
Writing a CellSpecification that includes all subtypes
Creating a DTO that covers all fields from the subtypes
Try the following:
var cells = (ICollection<Cell>)await _cellService.ListAsync();
result.AddRange(_mapper.Map<ICollection<Cell>, ICollection<CellDTO>>(cells));
Where _cellService is IService<Cell>

Blazor ProtectedSessionStorage object NULL

I've made a helper class on ProtectedSessionStorage to Read/Write data
using Microsoft.AspNetCore.ProtectedBrowserStorage;
public class SessionObjectHelper
{
[Inject] private ProtectedSessionStorage ProtectedSessionStore { get; set; }
public async Task<string> GetTestProperty()
{
var value = await ProtectedSessionStore.GetAsync<string>("TestProperty") ?? string.Empty;
return value;
}
public async Task SetTestProperty(string value)
{
await ProtectedSessionStore.SetAsync("TestProperty", value);
}
}
If I call any of these methods from a Component as illustrated below, ProtectedSessionStore is always NULL
[Inject] private SessionObjectHelper SessionObjectHelper { get; set; }
protected override async Task OnInitializedAsync()
{
var testProperty= await SessionObjectHelper.GetTestProperty();
}
Your SessionObjectHelper class should declare a constructor to get the injected service, not use the [Inject] attribute, e.g.
public class SessionObjectHelper
{
private ProtectedSessionStorage storage;
public SessionObjectHelper(ProtectedSessionStorage storage)
{
this.storage = storage;
}
The [Inject] attribute is designed to work with Razor Components, not regular classes.
You can then use
[Inject] public SessionObjectHelper SessionObjectHelper { get; set; }
In your component. Note I changed to public - if it's private the runtime won't be able to see/set it.

How to receive integration event from RabbitMQ broker using MassTransit?

I am trying to receive an event from the RabbitMQ broker but something wents wrong, the Consume method of my consumer is never called, although the message is visible on the bus. Here's my IntegrationEvent class:
public abstract class IntegrationEvent
{
protected IntegrationEvent(Guid entityId,
string eventType)
{
EntityId = entityId;
EventType = eventType;
}
public Guid Id { get; } = Guid.NewGuid();
public DateTime CreatedAtUtc { get; } = DateTime.UtcNow;
public Guid EntityId { get; }
public string EventType { get; }
public DateTime? PublishedAtUtc { get; set; }
}
And the example inheritor:
public sealed class UserCreatedIntegrationEvent : IntegrationEvent
{
public UserCreatedIntegrationEvent(Guid id,
string login,
string firstName,
string lastName,
string mailAddress)
: base(id,
nameof(UserCreatedIntegrationEvent))
{
Login = login;
FirstName = firstName;
LastName = lastName;
MailAddress = mailAddress;
}
public string Login { get; }
public string FirstName { get; }
public string LastName { get; }
public string MailAddress { get; }
}
Publication logic:
public async Task PublishAsync(params IntegrationEvent[] events)
{
var globalPublicationTasks = events
.Select(#event =>
{
#event.PublishedAtUtc = DateTime.UtcNow;
return _publishEndpoint.Publish(#event);
});
await Task.WhenAll(globalPublicationTasks);
}
Receiver classes and the dependencies registry code:
public sealed class IntegrationEventListener : BackgroundService
{
public IntegrationEventListener(IBusControl busControl,
IServiceProvider serviceProvider,
IOptions<RabbitMQSettings> busConfiguration)
: base(busControl,
serviceProvider,
busConfiguration,
NullLogger.Instance)
{
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var handler = BusControl
.ConnectReceiveEndpoint(BusConfiguration.HostName, receiveEndpointConfigurator =>
{
receiveEndpointConfigurator
.Consumer<IntegrationEventTransmitter>(ServiceProvider);
});
await handler.Ready;
}
catch (Exception e)
{
...
}
}
}
public sealed class IntegrationEventTransmitter : IntegrationEventHandler<IntegrationEvent>
{
public override async Task HandleAsync(IntegrationEvent #event)
{
throw new System.NotImplementedException();
}
}
public abstract class IntegrationEventHandler<TIntegrationEvent>
: IIntegrationEventHandler<TIntegrationEvent>,
IConsumer<TIntegrationEvent>
where TIntegrationEvent : IntegrationEvent
{
public async Task Consume(ConsumeContext<TIntegrationEvent> context) =>
await HandleAsync(context.Message);
public abstract Task HandleAsync(TIntegrationEvent #event);
}
...
.AddRabbitMQ(configuration,
ExchangeType.Fanout,
true)
.AddScoped<IntegrationEventTransmitter>()
.AddHostedService<IntegrationEventListener>();
...
internal static IServiceCollection RegisterRabbitMQDependencies(
this IServiceCollection services,
IConfiguration configuration,
string exchangeType)
{
var rabbitMQSettings = configuration
.GetSection(RabbitMQSettingsSectionKey)
.Get<RabbitMQSettings>();
services
.AddMassTransit(configurator =>
{
configurator.AddConsumers(typeof(IntegrationEventHandler<IntegrationEvent>).Assembly);
})
.AddSingleton(serviceProvider => MassTransit.Bus.Factory.CreateUsingRabbitMq(configurator =>
{
configurator
.Host(rabbitMQSettings.HostName,
rabbitMQSettings.VirtualHostName,
hostConfigurator =>
{
hostConfigurator.Username(rabbitMQSettings.UserName);
hostConfigurator.Password(rabbitMQSettings.Password);
});
configurator.ExchangeType = exchangeType;
}))
.AddSingleton<IPublishEndpoint>(provider => provider.GetRequiredService<IBusControl>())
.AddSingleton<ISendEndpointProvider>(provider => provider.GetRequiredService<IBusControl>())
.AddSingleton<IBus>(provider => provider.GetRequiredService<IBusControl>())
.Configure<RabbitMQSettings>(configuration.GetSection(RabbitMQSettingsSectionKey));
return services;
}
In the RabbitMQ management panel i can notice that message is being properly published on the bus, the consumer is also connected to the broker but for some reason it does not consume the message. What am i doing wrong?
You should not connect a receiving endpoint, as it's completely unnecessary in this case. As Chris mentioned, configuring MassTransit for ASP.NET Core is properly described in the documentation, and it makes total sense to follow the documentation to avoid unnecessary complexity.
In your particular case, you don't start the bus, although it's even mentioned in the Common Mistakes article as the first thing.
Just do the following:
Use AddMassTransit in Startup and configure the receive endpoint normally
Add the handler directly there, or use a consumer class instead. It does not need to be a background service, MassTransit will call it when it receives a message
Register the MassTransit host by calling AddMassTransitHostedService

no implicit reference conversion from 'System.Collections.Generic.List<Create.Command>' to 'MediatR.IRequest<MediatR.Unit>' + .NET Core + CQRS

I'm getting a List in the API controller method and passing it to the Handler like below.
What I am intending to do is loop over the list and save all the items of the list into the DB.
public class Create
{
public class Command : IRequest
{
public Guid A { get; set; }
public string B { get; set; }
public string C { get; set; }
public bool D { get; set; }
}
public class Handler : IRequestHandler<List<Command>>
{
private readonly DataContext _context;
private readonly IMapper _mapper;
public Handler(DataContext context, IMapper mapper)
{
_mapper = mapper;
_context = context;
}
public async Task<Unit> Handle(List<Command> request, CancellationToken cancellationToken)
{
// loop over the request list and save in the database
}
}
}
However there's a red line under 'Handler' in the code line: public class Handler : IRequestHandler<List<Command>>.
Hovering over the 'Handler', it says:
The type
'System.Collections.Generic.List'
cannot be used as type parameter 'TRequest' in the generic type or
method 'IRequestHandler'. There is no implicit reference
conversion from
'System.Collections.Generic.List'
to 'MediatR.IRequest'. [Application]csharp(CS0311)
My API Controller method is:
[HttpPost]
public async Task<ActionResult<Unit>> Create(List<Create.Command> commands) // not like this, it'll be a list
{
return await Mediator.Send(commands);
}
Red Line under return await Mediator.Send(commands); says:
Cannot implicitly convert type 'object' to
'Microsoft.AspNetCore.Mvc.ActionResult'. An explicit
conversion exists (are you missing a cast?) [API]csharp(CS0266)
If I've missed some information while writing the question, please be easy on me, I will keep updating upon inquiry.
So here's how I eventually solved the problem:
Step 1:
Instead of having props in the Command class, Have a nested class in the same Create.cs class where Command class is:
public class CreateDto
{
public Guid A { get; set; }
public string B { get; set; }
public string C { get; set; }
public bool D { get; set; }
}
Step 2: will be Command class. Command class will be now:
public class Command : IRequest
{
public List<CreateDto> SomeObjects { get; set; }
}
Step 3: Handler class will become:
public class Handler : IRequestHandler<Command>
{
private readonly DataContext _context;
private readonly IMapper _mapper;
public Handler(DataContext context, IMapper mapper)
{
_mapper = mapper;
_context = context;
}
public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
foreach (var obj in request.SomeObjectss)
{
// logic
}
return Unit.Value;
}
}
Step 4: Controller method will become:
[HttpPost]
public async Task<ActionResult<Unit>> Create(List<CreateDto> createDtos)
{
return await Mediator.Send(new Create.Command{SomeObjects = createDtos});
}

Avoid Casting in following Code by using Generics

I am new to generics and just wondering if it's possible to avoid the casting in the following code using better OO approach.
public class CollectorFactory
{
public static MyCollector Create(ICredential credential)
{
return new MyCollector(credential);
}
}
public class MyCollector {
public MyCredential Credential { get; set; }
public MyCollector(ICredential credential)
{
this.Credential = (MyCredential)credential;
}
public void Show()
{
Console.WriteLine(this.Credential.Username);
Console.WriteLine(this.Credential.AuthToken);
}
}
public class MyCredential : ICredential
{
public string Username{ get; set; }
public string AuthToken { get; set; }
}
public interface ICredential
{
}
Is there a way to save the casting of ICredential to MyCredential in MyCollector's Constructor? I don't have option to put Username and AuthToken in ICredential as it's implemented by two different Credentials that both have different set of properties. CollectorFactory will be returning different MyCollector instances in the future and both need to have different credentials.
Any help would be really appreciated.
I don't think it's possible given that you're implementing different credentials and trying to use them for ICredential as well.
Here is a way of doing this using generics. Please read my comments in the code.
public class CollectorFactory<T>
{
public T Create(ICredential credential)
{
return (T)Activator.CreateInstance(typeof(T), credential);
}
}
public class MyCollector : BaseCollector
{
public dynamic Credential { get; private set; }
public MyCollector(ICredential credential)
: base(credential)
{
this.Credential = credential;
}
// Having this method here limits your ability to make it more generic.
// Consider moving this to MyCredential since it refers to specific properties in MyCredential.
// If that is not what you want, then you must do a type check before calling methods/ accessing props in Credentials.
public void Show()
{
Console.WriteLine(this.Credential.Username);
Console.WriteLine(this.Credential.AuthToken);
}
}
public class MyCredential : ICredential
{
public string Username { get; set; }
public string AuthToken { get; set; }
}
public abstract class BaseCollector : ICredentialCollector
{
protected BaseCollector(ICredential credential)
{
if (credential == null)
{
throw new ArgumentNullException(nameof(credential));
}
}
}
public interface ICredentialCollector
{
}
public interface ICredential
{
}
// test implementation
public class TestClass
{
public void AuthFactoryTest()
{
// test auth instance
MyCredential auth = new MyCredential() {AuthToken = "asfgasdgdfg", Username = "xuser"};
// Create test factory
var fact = new CollectorFactory<MyCollector>();
var myCollector = fact.Create(auth);
// Do what you need to do to collector object
myCollector.Show();
}
}
Generics isn't the solution in this case. The issue here is that your factory is returning a specific type (MyCollector). A solution around this would be the following:
public class CollectorFactory
{
public static ICollector Create(MyCredential credential)
{
return new MyCollector(credential);
}
public static ICollector Create(OtherCredential credential)
{
return new OtherCollector(credential);
}
}
public interface ICollector
{
void Show();
}
public class MyCollector : ICollector
{
public MyCredential Credential { get; set; }
public MyCollector(MyCredential credential)
{
this.Credential = credential;
}
public void Show()
{
Console.WriteLine(this.Credential.Username);
Console.WriteLine(this.Credential.AuthToken);
}
}
public class MyCredential : ICredential
{
public string Username{ get; set; }
public string AuthToken { get; set; }
}
public interface ICredential
{
}
The above is pretty much the canonical example of the Factory design pattern.
Instead of overloads you could also do typechecking in the factory:
public class CollectorFactory
{
public static ICollector Create(ICredential credential)
{
if(credential.GetType() == typeof(MyCredential))
return new MyCollector((MyCredential) credential);
if(credential.GetType() == typeof(OtherCredential ))
return new OtherCollector((OtherCredential ) credential);
}
}

Categories