Search code examples
c#interfacecastingactivatorcreateinstance

Casting to correct type after using Activator.CreateInstance


I am having issues casting to correct type after using Activator.CreateInstance(...) which just returns an object type. So the code I have is as follows. I have an empty interface to define an object as a command..

/// <summary>
/// Defines the expected members for a Command object
/// </summary>
public interface ICommand { }

I have a command that implements that interface

/// <summary>
/// Contains command data for adding a company
/// </summary>
public class CompanyAddCommand : ICommand
{
    #region Properties

    public bool IsActive { get; protected set; }
    public string CompanyName { get; protected set; }
    public DateTime RecordCreatedDateTime { get; protected set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="CompanyAddCommand"/> class.
    /// </summary>
    /// <param name="isActive">if set to <c>true</c> [is active].</param>
    /// <param name="companyName">Name of the company.</param>
    /// <param name="recordCreatedDateTime">The record created date time.</param>
    public CompanyAddCommand(bool isActive,
        string companyName, DateTime recordCreatedDateTime)
    {
        IsActive = isActive;
        CompanyName = companyName;
        RecordCreatedDateTime = recordCreatedDateTime;
    }

    #endregion
}

I have a command validator interface that has a single method Validate() and has a type parameter which is an object that must be a class and implement ICommand interface.

public interface IValidationHandler<in TCommand> 
    where TCommand : class, ICommand
{
    /// <summary>
    /// Validates the specified command.
    /// </summary>
    /// <param name="command">The command.</param>
    void Validate(TCommand command);
}

I have a command validator class that implements IValidationHandler

public class CompanyAddValidator : ValidatorBase, IValidationHandler<CompanyAddCommand>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="CompanyAddValidator"/> class.
    /// </summary>
    public CompanyAddValidator(IUnitOfWork unitOfWork)
        : base(unitOfWork)
    { }

    #endregion

    #region IValidationHandler<CompanyAddCommand> Members

    /// <summary>
    /// Validates the specified command.
    /// </summary>
    /// <param name="command">The command.</param>
    public void Validate(
        CompanyAddCommand command)
    {
        ValidateCompanyEntity(command);
        ValidationUniqueCompanyName(command);
    }

    #endregion

    /// <summary>
    /// Validates the company entity.
    /// </summary>
    /// <param name="command">The command.</param>
    private void ValidateCompanyEntity(CompanyAddCommand command)
    {
        // do some work            
    }

    /// <summary>
    /// Validations the name of the unique company.
    /// </summary>
    /// <param name="command">The command.</param>
    private void ValidationUniqueCompanyName(CompanyAddCommand command)
    {
        // do some work;
    }
}

I have a command bus that on construction scans the assembly for all classes that are handler types and registers these types as either command handlers or validation handlers in one of two dictionarys where the key is the command type and the value is the handler type.

public class CommandBus : ICommandBus
{
    #region Fields

    private Dictionary<Type, Type> _commandHandlerTypes;
    private Dictionary<Type, Type> _validationHandlerTypes;

    private readonly IUnitOfWork _unitOfWork;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandBus"/> class.
    /// </summary>
    public CommandBus(IUnitOfWork unitOfWork)
    {
        // Validate arguments
        if (unitOfWork == null) throw new ArgumentNullException("unitOfWork");

        // Cache the UOW
        _unitOfWork = unitOfWork;

        RegisterAllHandlerTypes();
    }

    #endregion

    #region ICommandBus Members

    /// <summary>
    /// Submits the specified command.
    /// </summary>
    /// <typeparam name="TCommand">The type of the command.</typeparam>
    /// <param name="command">The command.</param>
    /// <returns></returns>
    public ICommandResult Submit<TCommand>(TCommand command) 
        where TCommand : class, ICommand
    {
        Validate(command);
        return SubmitInternal(command);
    }

    #endregion

    #region Methods : private

    /// <summary>
    /// Gets the command handler types.
    /// </summary>
    /// <returns></returns>
    private static Dictionary<Type, Type> GetCommandHandlerTypesFromAssembly()
    {
        // Create a function that will return true if the type is of type ICommandHandler
        Func<Type, bool> isCommandHandler = t => t.GetInterfaces()
           .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommandHandler<>));

        // Use this function to register the command handlers in this assembly
        Assembly assembly = Assembly.GetCallingAssembly();
        return GetSpecificHandlerTypesFromAssembly(assembly, isCommandHandler);
    }

    /// <summary>
    /// Registers all of the handler types.
    /// </summary>
    private void RegisterAllHandlerTypes()
    {
        _commandHandlerTypes = GetCommandHandlerTypesFromAssembly();
        _validationHandlerTypes = GetValidationHandlerTypesFromAssembly();
    }

    /// <summary>
    /// Gets the specific handler types using the specified function to determine correct types.
    /// </summary>
    /// <param name="assembly">The assembly to get hansdlers from</param>
    /// <param name="isCorrectTypeCallback">A function that returns true if the Type is correct.</param>
    /// <returns></returns>
    private static Dictionary<Type, Type> GetSpecificHandlerTypesFromAssembly(
        Assembly assembly, 
        Func<Type, bool> isCorrectTypeCallback)
    {
        Func<Type, IEnumerable<Tuple<Type, Type>>> collect = t => t.GetInterfaces()
            .Select(i => Tuple.Create(i.GetGenericArguments()[0], t));

        return assembly
            .GetTypes()
            .Where(t => !t.IsAbstract && !t.IsGenericType)
            .Where(isCorrectTypeCallback)
            .SelectMany(collect)
            .ToDictionary(x => x.Item1, x => x.Item2);
    }

    /// <summary>
    /// Gets the validation handlers.
    /// </summary>
    /// <returns></returns>
    private static Dictionary<Type, Type> GetValidationHandlerTypesFromAssembly()
    {
        // Create a function that will return true if the type is of type IValidationHandler
        Func<Type, bool> isValidationHandler = t => t.GetInterfaces()
           .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidationHandler<>));

        // Use this function to register the validation handlers in this assembly
        Assembly assembly = Assembly.GetCallingAssembly();
        return GetSpecificHandlerTypesFromAssembly(assembly, isValidationHandler);
    }

    private ICommandResult SubmitInternal(object command)
    {
        // do some work
    }

    private void Validate<TCommand>(TCommand command)
        where TCommand : class, ICommand
    {
        // Get the command type and check if we have a validator for that type
        Type commandType = command.GetType();
        bool hasValidator = _validationHandlerTypes.ContainsKey(commandType);

        // If we don't have a validator dont worry, just break leve the method
        if (!hasValidator) return;

        // Create and instance of the handler
        Type validatorType = _validationHandlerTypes[commandType];
        var handler = Activator.CreateInstance(validatorType, _unitOfWork);
        Type handlerType = handler.GetType();
        var handlerB = handler as IValidationHandler<TCommand>;
        var handlerC = handler as IValidationHandler<ICommand>;

        //handler.Validate(command); compiler fails as handler is OBJECT

        if (handlerB != null) handlerB.Validate(command);
        if (handlerC != null) handlerC.Validate(command);
    }    

    #endregion
}

The problem I have is in the Validate() method although I can create an instance of my command validator and it shows (hovering over the variable) that it is the correct type (i.e. an instance of CompanyAddValidator) I cannot call the Validate() method as it is an object. If I try to cast this handler as either IValidationHandler < TCommand > or IValidationHandler < ICommand > (i.e. handlerB and handlerC) so I can call the "Validate()" method on the interface I get a null reference.

I cannot get the right syntax that allows the level of genericism that I require AND be able to call the Validate() method on an instantiated CompanyAddValidator object or any other domain entity validator that I may have.

Can any one point me in the right direction, please? I tried to follow these examples but have had no luck.

http://stackoverflow.xluat.com/questions/30739865/command-bus-dispatcher-and-handler-registration-without-dependency-injection

Can I use Activator.CreateInstance with an Interface?

https://alertcoding.wordpress.com/2013/03/18/command-and-query-based-entity-framework-architecture-part-2/comment-page-1/#comment-153


Solution

  • You can do this:

    dynamic handler = Activator.CreateInstance(validatorType, _unitOfWork);
    handler.Validate((dynamic)command);
    

    Or you can do this instead:

    handler.GetType().GetMethod("Validate").Invoke(handler,new object[] {command});
    

    Note: If you have not read it before, I suggest your read how to write Minimal, Complete, and Verifiable example. The code that you've given, is quire inconsistent, for example, you are trying to call handler.Validate with no parameters, where as the method is defined with one parameter, your Submit method on CommandBus class returns nothing whereas it's signature requires result type of ICommandResult et cetra, et cetra. This makes it more difficult to answer your question.