lundi 31 août 2020

Cant make an appropriate abstraction when dealing with generic method (C#)

Im trying to implement a wrapper for using System.Data.SQLite features in order to get rid of duplicate code in a good OOP manner. So, i have the following generic method:

    public T SendSelectQuery<T>(string sql, Func<SQLiteDataReader, T> processResult) where T : IDBResult
    {
        try
        {
            using (var dbConnection = new SQLiteConnection("path"))
            using (var cmd = new SQLiteCommand(sql, dbConnection))
            {
                dbConnection.Open();

                cmd.CommandType = CommandType.Text;

                using (SQLiteDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    return processResult(rdr);
                }
            }
        }
        catch (Exception ex)
        {
            return T??????
        }
    }

T is a result object, i.e.:

    public interface IDBResult
{
    bool Completed { get; set; }
    string Exception { get; set; }
}

public abstract class CustomDBREsult : IDBResult
{
    public bool Completed { get; set; }
    public string Exception { get; set; }

    public string Payload { get; set; }
    public CustomDBREsult(bool Completed, string exception, string Payload)
    {
        this.Completed = Completed;
        this.Exception = exception;
        this.Payload = Payload;
    }
}

public class SuccessCustomDBResult : CustomDBREsult
{
    public SuccessCustomDBResult(string Payload) : base(true, string.Empty, Payload)
    {

    }
}
public class ErrorCustomDBResult : CustomDBREsult
{
    public ErrorCustomDBResult() : base(false, "exception", string.Empty)
    {

    }
}

I want to call SendSelectQuery<CustomDBREsult>(...) and get an instance of CustomDBREsult child.

As you may have already noticed the problem occurs in catch segment where i need to return a T object, but i cant instantiate an approprite Error object derived from CustomDBREsult.

I could change return type of SendSelectQuery<T> to IDBResult and return in catch segment smth like this:

    public class DefaultDBError : IDBResult
    {
        public bool Completed { get; set; } = false;
        public string Exception { get; set; } = "db exception";
    }

But in this case i would need to cast the result of SendSelectQuery<T> from IDBResult to T. And it doesn't seem to be a very good practice.

    IDBResult res = DBMethods.SendSelectQuery<CustomDBREsult>("sql query", processResult);
    if (res is CustomDBREsult cdbres)
    {

    }
    else if (res is DefaultDBError ddberror)
    {

    }

The other option is to "lift up" try catch block and use SendSelectQuery<T> inside it, but i would need to duplicate this block everywhere i use SendSelectQuery<T> and still convert IDBResult to T.

If smn managed to understand me, would appreciate your comments. I guess my problem is making good abstractions of logic.

Aucun commentaire:

Enregistrer un commentaire