Oracle ManagedDataAccess.EntityFramework Database.SqlQuery绑定参数的位置?

Jam*_* R. 5 c# oracle entity-framework

我有以下代码:

        var query = Database.SqlQuery<int>(@"

            SELECT CASE WHEN EXISTS (
                SELECT 1 
                FROM v$session v, UsersXxxx u
                WHERE v.Client_Info LIKE u.UserName || ';%' 
                AND v.UserName = :schemaName
                AND u.SchemaName = :schemaName
                AND v.module = 'XXXX.exe' 
                AND u.UserKey = :userKey)
            THEN 1 ELSE 0 END AS LoggedIn FROM DUAL",

            new OracleParameter("schemaName", schemaName),
            new OracleParameter("userKey", userKey));

        return query.First() != 0;
Run Code Online (Sandbox Code Playgroud)

这产生了"ORA-01008:并非所有变量都受到约束".我怀疑变量被绑定的方式出现了问题并最终尝试了这个:

        var query = Database.SqlQuery<int>(@"

            SELECT CASE WHEN EXISTS (
                SELECT 1 
                FROM v$session v, UsersXxxx u
                WHERE v.Client_Info LIKE u.UserName || ';%' 
                AND v.UserName = :schemaName
                AND u.SchemaName = :schemaName
                AND v.module = 'XXXX.exe' 
                AND u.UserKey = :userKey)
            THEN 1 ELSE 0 END AS LoggedIn FROM DUAL",

            new OracleParameter("asdf", schemaName),
            new OracleParameter("fdsa", schemaName),
            new OracleParameter("userKey", userKey));

        return query.First() != 0;
Run Code Online (Sandbox Code Playgroud)

这就像一个魅力!我在文档周围戳了一下,发现了一个说:

"ODP.NET和Entity Framework支持绑定标量参数.在Entity Framework中,支持按名称进行参数绑定.不支持按位置绑定."

不知何故,我认为文档对我说谎,并且它试图按位置绑定.我记得很久以前在EF支持之前解决这个问题,但我不记得修复是什么,更不用说如何在EF中应用相同的技术了.

我的解决方法,虽然kludgy,工作但是没有一个选项,使其通过名称而不是位置绑定?如果是这样,它是什么?

Iva*_*oev 8

问题是Database.SqlQuery方法使用CreateCommand底层的方法DbConnection.在ODP.NET中,这导致OracleCommand默认情况下通过position(BindByName = false)绑定参数.

这种行为是不可配置的,没有改变它的好地方.作为一种解决方法,我可以建议使用自定义SqlQuery方法替换,这将创建OracleCommand使用BindByName = true,执行ExecuteReader和使用ObjectContext.Translate方法来执行映射:

public static class EFExtensions
{
    public static IEnumerable<T> DbQuery<T>(this DbContext db, string sql, params object[] parameters)
    {
        if (parameters != null && parameters.Length > 0 && parameters.All(p => p is OracleParameter))
            return OracleDbQuery<T>(db, sql, parameters);
        return db.Database.SqlQuery<T>(sql, parameters);
    }

    private static IEnumerable<T> OracleDbQuery<T>(DbContext db, string sql, params object[] parameters)
    {
        var connection = db.Database.Connection;
        var command = connection.CreateCommand();
        ((OracleCommand)command).BindByName = true;
        command.CommandText = sql;
        command.Parameters.AddRange(parameters);
        connection.Open();
        try
        {
            using (var reader = command.ExecuteReader())
            using (var result = ((IObjectContextAdapter)db).ObjectContext.Translate<T>(reader))
            {
                foreach (var item in result)
                    yield return item;
            }
        }
        finally
        {
            connection.Close();
            command.Parameters.Clear();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为了使用它,只需更换

context.Database.SqlQuery<..>(...)
Run Code Online (Sandbox Code Playgroud)

打电话给

context.DbQuery<..>(...)
Run Code Online (Sandbox Code Playgroud)


Jus*_*tin 7

在托管驱动程序的最新版本中,您可以添加一个web.config条目,默认情况下将 BindByName 设置为 true。

<oracle.manageddataaccess.client>
<version number="*">
  <settings>
    <setting name="BindByName" value="true" />
  </settings>
</version>
</oracle.manageddataaccess.client>
Run Code Online (Sandbox Code Playgroud)