jonwagner / Insight.Database

Fast, lightweight .NET micro-ORM
Other
859 stars 145 forks source link

C# 8 - Auto Interface Implementation + Default interface methods Support #411

Closed keerthirajap closed 4 years ago

keerthirajap commented 4 years ago

Please provide support for executing code inside Default interface methods in c# 8 along with Auto Interface Implementation. When I tried it executing SQL inside interface method throw error.

InvalidOperationException: The stored procedure 'GetBeerByTypeDefault' doesn't exist

InvalidOperationException: The stored procedure 'GetBeerByTypeDefault' doesn't exist.
Insight.Database.Providers.Default.SqlParameterHelper.DeriveParameters(SqlCommand cmdToPopulate)
Insight.Database.SqlInsightDbProvider.DeriveParametersFromStoredProcedure(IDbCommand command)
Insight.Database.Providers.InsightDbProvider+<>c__DisplayClass12_0.<DeriveParameters>b__0(IDbConnection _)
Insight.Database.DBConnectionExtensions.ExecuteAndAutoClose<T>(IDbConnection connection, Func<IDbConnection, IDbCommand> getCommand, Func<IDbCommand, IDataReader, T> translate, CommandBehavior commandBehavior)
Insight.Database.Providers.InsightDbProvider.DeriveParameters(IDbCommand command)

Asp.Net Core 3.0 + MVC, Insight.Database 6.2.10

Steps to reproduce

IBeerRepository.cs

 public interface IBeerRepository : IDbConnection
    {
        public IDbConnection GetConnection();

        [Sql("SELECT * FROM Beer")]
        Task<IList<Beer>> GetBeerByType(); // This works perfect

        Task<IList<Beer>> GetBeerByTypeDefault()
        {          
            return GetConnection().QueryAsync<Beer>("SELECT * FROM Beer"); //Throws error
        }
    }

Beer.cs

public class Beer
    {
        public int ID;
        public string Type;
    }

HomeController.cs

 public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IBeerRepository _IBeerRepository;

        public HomeController(ILogger<HomeController> logger)
        {
            DbConnection c = new SqlConnection("Data Source=.;Initial Catalog=InsightDatabase;Integrated Security=True");
            _IBeerRepository = c.As<IBeerRepository>();
            _logger = logger;
        }

        public async Task<IActionResult> Index()
        {
            var results = await _IBeerRepository.GetBeerByType();
            var results1 = await _IBeerRepository.GetBeerByTypeDefault();

            return View();
        }
}

SQL Create table : CREATE TABLE Beer ([ID] [int], [Type] varchar(128))

resnikb commented 4 years ago

Can you please clarify what you're trying to achieve here?

Default interface implementations only apply if the implementing class does not implement the methods. In a way, they are meant to provide compatibility for implementing classes if the interface changes, especially when interface owner has no control over the implementations.

In Insight's case, auto-implementing an interface means implementing the methods, in which case the default implementation will not be invoked, as per C# specification. It seems to me that your usage would suit better to an extension method, and not a default implementation.

For example:

    public interface IBeerRepository : IDbConnection
    {
        public IDbConnection GetConnection();

        [Sql("SELECT * FROM Beer")]
        Task<IList<Beer>> GetBeerByType();
    }

    public static class BeerRepositoryExtensions
    {
        public static Task<IList<Beer>> GetBeerByTypeDefault(this IBeerRepository repo)
        {          
            return repo.GetConnection().QueryAsync<Beer>("SELECT * FROM Beer");
        }
    }
jonwagner commented 4 years ago

Also, please note that you're calling the wrong Insight method. If you want to pass SQL to the server (and not a stored proc name), then you should use the API method that ends with Sql:

        Task<IList<Beer>> GetBeerByTypeDefault()
        {          
            return GetConnection().QueryAsync<Beer>("SELECT * FROM Beer"); //Throws error
        }

Should be:

            return GetConnection().QueryAsyncSql<Beer>("SELECT * FROM Beer"); //Throws error
keerthirajap commented 4 years ago

Still getting same error after changing to QueryAsyncSql


 public interface IBeerRepository : IDbConnection
    {
        public IDbConnection GetConnection();

        [Sql("SELECT * FROM Beer")]
        Task<IList<Beer>> GetBeerByType(); // This works perfect

        Task<IList<Beer>> GetBeerByTypeDefault()
        {
            return GetConnection().QuerySqlAsync<Beer>("SELECT * FROM Beer"); //Throws error
        }
    }

InvalidOperationException: The stored procedure 'GetBeerByTypeDefault' doesn't exist

InvalidOperationException: The stored procedure 'GetBeerByTypeDefault' doesn't exist. Insight.Database.Providers.Default.SqlParameterHelper.DeriveParameters(SqlCommand cmdToPopulate) Insight.Database.SqlInsightDbProvider.DeriveParametersFromStoredProcedure(IDbCommand command) Insight.Database.Providers.InsightDbProvider+<>c__DisplayClass12_0.b_0(IDbConnection ) Insight.Database.DBConnectionExtensions.ExecuteAndAutoClose(IDbConnection connection, Func<IDbConnection, IDbCommand> getCommand, Func<IDbCommand, IDataReader, T> translate, CommandBehavior commandBehavior) Insight.Database.Providers.InsightDbProvider.DeriveParameters(IDbCommand command)

jonwagner commented 4 years ago

I was able to filter out non-abstract methods on interfaces when the code generator runs.

So now if you want to implement methods on interfaces, Insight won't override them.

jonwagner commented 4 years ago

Fixed in next build.