Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use multiple IMethodCallTranslatorProvider #23275

Closed
thefat32 opened this issue Nov 11, 2020 · 4 comments
Closed

How to use multiple IMethodCallTranslatorProvider #23275

thefat32 opened this issue Nov 11, 2020 · 4 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@thefat32
Copy link

Ask a question

Hello, we are using Oracle provider for EF Core 3.1 and would like to add our custom IMethodCallTranslatorProvider so we can extend more functions to be translated as SQL.

Adding our translator through

        void IDbContextOptionsExtension.ApplyServices(IServiceCollection services)
        {
            services.AddSingleton<IMethodCallTranslatorProvider, CustomMethodCallTranslatorPlugin>();
        }

makes EF completly ignore Oracle's OracleMethodCallTranslatorProvider

Is there a way to register another provider or custom translators to be used alongside Oracle's ?

Right now our solution is to male our coustom provider to extend OracleMethodCallTranslatorProvider, but this is not ideal as this class is internal and could change on a future Oracle SQL Proovider update

Include your code

  • DbContextOptionsBuilder Extension
    public static class DbContextOptionsBuilderExtensions
    {
        public static DbContextOptionsBuilder UseCustomExtensionFunctions(
            this DbContextOptionsBuilder optionsBuilder)
        {
            var extension = GetOrCreateExtension(optionsBuilder);
            ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

            return optionsBuilder;
        }

        private static DbContextOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder.Options.FindExtension<DbContextOptionsExtension>()
               ?? new DbContextOptionsExtension();
    }
  • IDbContextOptionsExtension
    public class DbContextOptionsExtension : IDbContextOptionsExtension
    {
        private DbContextOptionsExtensionInfo _info;

        public void Validate(IDbContextOptions options)
        {
        }

        public DbContextOptionsExtensionInfo Info
        {
            get
            {
                return this._info ??= (new MyDbContextOptionsExtensionInfo(this));
            }
        }

        void IDbContextOptionsExtension.ApplyServices(IServiceCollection services)
        {
            services.AddSingleton<IMethodCallTranslatorProvider, CustomMethodCallTranslatorPlugin>();
        }

        private sealed class MyDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo
        {
            public MyDbContextOptionsExtensionInfo(IDbContextOptionsExtension instance) : base(instance) { }

            public override bool IsDatabaseProvider => false;

            public override string LogFragment => "";

            public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
            {

            }

            public override long GetServiceProviderHashCode()
            {
                return 0;
            }
        }
    }
  • IMethodCallTranslatorProvider (now extending OracleMethodCallTranslatorProvider)
    public sealed class CustomMethodCallTranslatorPlugin : OracleMethodCallTranslatorProvider
    {
        public CustomMethodCallTranslatorPlugin(RelationalMethodCallTranslatorProviderDependencies dependencies)
            : base(dependencies)
        {
            ISqlExpressionFactory expressionFactory = dependencies.SqlExpressionFactory;
            this.AddTranslators(new List<IMethodCallTranslator>
            {
                new ConvertToIntTranslator(expressionFactory),
                new StringSubStringTranslator(expressionFactory)
            });
        }
    }
@smitpatel
Copy link
Contributor

Use true plugin infrastructure. Rather than replacing service of IMethodCallTranslatorProvider, add a service implementation for IMethodCallTranslatorPlugin. See https://github.com/dotnet/efcore/blob/main/src/EFCore.Sqlite.NTS/Query/Internal/SqliteNetTopologySuiteMethodCallTranslatorPlugin.cs for example.

@thefat32
Copy link
Author

thefat32 commented Nov 11, 2020

Perfect! Just extending IMethodCallTranslatorPlugin worked like a charm!

    public class CustomMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
    {
        public CustomMethodCallTranslatorPlugin(ISqlExpressionFactory sqlExpressionFactory)
        {
            Translators = new IMethodCallTranslator[]
            {
                new ConvertToIntTranslator(sqlExpressionFactory),
                new StringSubStringTranslator(sqlExpressionFactory)
            };
        }

        public virtual IEnumerable<IMethodCallTranslator> Translators { get; }
    }

and adding the service with

        void IDbContextOptionsExtension.ApplyServices(IServiceCollection services)
        {
            services.AddSingleton<IMethodCallTranslatorPlugin, CustomMethodCallTranslatorPlugin>();
        }

Thank you very much!

@smitpatel smitpatel added the closed-no-further-action The issue is closed and no further action is planned. label Nov 11, 2020
@roji
Copy link
Member

roji commented Nov 12, 2020

If you're looking for simple additional function translations, there's also the approach of using DbFunction, which may be lighter/more appropriate. The docs for that are still in progress (dotnet/EntityFramework.Docs#500), but it entails invoking modelBuilder.HasDbFunction in your model's OnModelCreating and configuring things there.

@thefat32
Copy link
Author

I've also seen this approach. I think for now is better for us to use IMethodCallTranslatorPlugin. It let us organize our code better and is pretty straight foward to add a new translator once the extensions are coded.

Thanks for your input!

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

4 participants