简体   繁体   中英

Leaking Entity Framework's abstraction - just a little

What if I want access to a feature unique to the RDBMS? Is there a way to inject DB specific SQL into the SQL generated by EF?

For example, in Oracle 12c you can add temporal validity to DDL and DML (there is a lot of variety; I'll use only simple examples here):

This could be nicely modeled in C#:

[TemporalAxis("valid_time")]
public class SomeEntity
{
  public string SomeField { get; set; }
}

Then used with LINQ

var results = context.SomeEntities
                .Where(i => i.SomeField = "some_value")
                .AsOfPeriodFor("valid_time", dateVariable);

The .AsOfPeriodFor extension can be something like this:

public static class TemporalExtensions
{
    public static IEnumerable<TSource> AsOfPeriodFor<TSource>(this IEnumerable<TSource> source, 
        string temporalAxis, DateTime asOfDate)
    {
        // reflect on TSource to ensure it has the correct attribute/value (else degrade/throw)
        // do something to source that sets up EF to add the extra clause in the DML

        return source;
    }
}

And I guess the DbContext can reflect on its entities to drive the DDL at initialization time (I'll have to learn more about this).

The result of the above is EF will emit the following SQL

DDL (at initialize time):

create table some_table
  some_field varchar2(30)

  period for valid_time -- Need to inject this at DB Initialize time
);

DML (at query time):

select
  some_field
from 
  some_table
  as of period for valid_time to_timestamp('27-Oct-14') -- and this at query time
where
  some_field = 'some_value';

My question: Are there hooks or IInterfaces available to inject these RDBMS specialty phrases into the SQL generated by EF? Or will have to marry the above with a custom Oracle DB Provider? Is this type of thing possible and can you point me to a blog/book/video/guru that can provide guidance?

There is no way, to my knowledge, to modify the SQL that gets generated by an EF provider.

However, for those special cases, you can run SQL directly.

context.Database.SqlQuery<SomeEntity>("select * from SomeEntity " +
"some more custom sql here " +
"where somecomlumn = @p1", parameter1);

You just have to make sure whatever you return matches the shape of SomeEntity.

Implement an Interceptor: EF-Tutorial

Looks like this:

class EFCommandInterceptor: IDbCommandInterceptor
        {
            public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                LogInfo("NonQueryExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText));
            }

            public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                LogInfo("NonQueryExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync,  command.CommandText));
            }

            public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContextt<System.Data.Common.DbDataReader> interceptionContext)
            {
                LogInfo("ReaderExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText));
            }

            public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
            {
                LogInfo("ReaderExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText));
            }

            public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                LogInfo("ScalarExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText));
            }

            public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                LogInfo("ScalarExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText));
            }

            private void LogInfo(string command, string commandText)
            {
                Console.WriteLine("Intercepted on: {0} :- {1} ", command, commandText);
            }
        }

But how to get the type to get the metadata i do not know by now.

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