简体   繁体   中英

C# Interface: Handling instance classes using generic interface

We are trying to build a web api request to handle different type of business actions in a common endpoint. We have got a parent model class which holds common fields, and each business type has its own class inheriting from base model class. Controller input type will be the base class and client must specify the type being sent in the request (designated enum value for each type, not the fully qualified type name). We have created a custom de-serialiser to convert the received object to actual type based on the specified type.

Like model classes, we have got base interface inherited by each business type. Since the actions are same across different business types and only the input and output types of each action varies, we defined a generic base interface holding all the actions. Each business specific class inherits interface by specifying relevant model type during inheritance.

Problem trying to solve:

I know the model type through custom de-serialiser. Based on this type I must get the instance of class inheriting the interface for the identified type and call the method of that service. How to achieve this?

Written below method but getting runtime exception for invalid casting

private static ITransactionService<Transaction> GetServiceInstance(Transaction transaction)
{
    return transaction switch
    {
        Commitment => (ITransactionService<Transaction>)new CommitmentService(),
        Drawdown => (ITransactionService<Transaction>)new DrawdownService(),
        _ => null
    };
}

Below is the complete code structure for reference:

public class Transaction
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
}
public class Commitment : Transaction
{
    public string Investor { get; set; }
    public int Quantity { get; set; }
}
public class Drawdown : Transaction
{
    public decimal Amount { get; set; }
}
public interface ITransactionService<T> where T : Transaction
{
    T Create(T transaction);
    T Update(T transaction);
    T Apporve(T transaction);
}
public class CommitmentService : ITransactionService<Commitment>
{
    public Commitment Apporve(Commitment transaction)
    {
        throw new NotImplementedException();
    }

    public Commitment Create(Commitment transaction)
    {
        throw new NotImplementedException();
    }

    public Commitment Update(Commitment transaction)
    {
        throw new NotImplementedException();
    }
}
public class DrawdownService : ITransactionService<Drawdown>
{
    public Drawdown Apporve(Drawdown transaction)
    {
        throw new NotImplementedException();
    }

    public Drawdown Create(Drawdown transaction)
    {
        throw new NotImplementedException();
    }

    public Drawdown Update(Drawdown transaction)
    {
        throw new NotImplementedException();
    }
}

The exception I am getting:

在此处输入图像描述

Basically we are trying to reduce number of classes and endpoints need to be created for each type.

You have a variance problem, ITransactionService<Commitment> can't be cast to ITransactionService<Transaction> . Some variance problems can be solved by adding in or out keywords .

But in general all variance problems can be solved with either more generics. Forcing the caller to either know the actual type, or invoke the methods via reflection;

private static ITransactionService<T> GetServiceInstance<T>(T transaction)

Or more types with less generics. Adding methods that allow the caller to access features without needing to know the actual type;

public interface ITransactionService
{
    Transaction Create(Transaction transaction);
    Transaction Update(Transaction transaction);
    Transaction Apporve(Transaction transaction);
}
public interface ITransactionService<T> : ITransactionService where T : Transaction
{
    T Create(T transaction);
    T Update(T transaction);
    T Apporve(T transaction);
}
public abstract class BaseService<T> : ITransactionService<T> where T : Transaction
{
    Transaction ITransactionService.Apporve(Transaction transaction)
        => Apporve((T)transaction);
    public abstract T Apporve(T transaction);

    Transaction ITransactionService.Create(Transaction transaction)
        => Create((T)transaction);
    public abstract T Create(T transaction);

    Transaction ITransactionService.Update(Transaction transaction)
        => Update((T)transaction);
    public abstract T Update(T transaction);
}

Now every ITransactionService<T> has a known base type, ITransactionService that you can cast.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM