Entity Framework ObjectContext -> необработанные вызовы SQL к собственной СУБД

У меня есть приложение, использующее структуру сущностей ADO.NET (версия VS2008, а не более новая и более крутая), и мне нужно иметь возможность вызывать базовую СУБД (это postgres), чтобы вызвать некоторый SQL, который Entity Фреймворк не поддерживает.

Есть ли способ перейти от Entity Framework ObjectContext к чему-то, что позволит мне выполнять необработанный SQL? (Мне нужно запустить TRUNCATE TABLE перед вставкой) Я согласен с хакерским решением (например, вытащить информацию о строке подключения СУБД из EF и использовать ее для создания соединения с помощью провайдера postgres ADO.NET), но не хотите управлять двумя наборами строк подключения (один для инфраструктуры сущностей, один для ADO.NET).

Я знаю об ограничениях первой версии Entity Framework, но это не стоит вложений, необходимых для переключения этого приложения на другую ORM, и использование EF 4.0 также не вариант.

Есть идеи?

Кстати, это тот же вопрос, что и Возможно ли запустить собственный sql с инфраструктурой сущностей?, но обходной путь, описанный в этом ответе, мне не подойдет, поскольку мне действительно нужно выполнить необработанный SQL.


person Justin Grant    schedule 16.10.2009    source источник


Ответы (4)


Ответ Крейга, хотя он и не работал как есть, заставил меня посмотреть в правильном направлении. Оказывается, есть свойство EntityConnection.StoreConnection, которое обеспечивает подключение к базовой СУБД. Таким образом, выполнить «родной» SQL так же просто:

    static void ExecuteSql(ObjectContext c, string sql)
    {
        var entityConnection = (System.Data.EntityClient.EntityConnection)c.Connection;
        DbConnection conn = entityConnection.StoreConnection;

        ConnectionState initialState = conn.State;
        try
        {
            if (initialState != ConnectionState.Open)
                conn.Open();  // open connection if not already open
            using (DbCommand cmd = conn.CreateCommand())
            {
                cmd.CommandText = sql;
                cmd.ExecuteNonQuery();
            }
        }
        finally
        {
            if (initialState != ConnectionState.Open)
                conn.Close(); // only close connection if not initially open
        }
    }
person Justin Grant    schedule 16.10.2009
comment
Однако это не сработает, если объектное соединение находится в середине транзакции. - person Eamon Nerbonne; 21.07.2011
comment
@Justin Grant Привет, я думал, что ExecuteSql использует то же соединение с БД, что и c? Потому что я хочу, чтобы они были в одной транзакции. Благодарю. - person Joe.wang; 14.12.2012
comment
2013 год EF 5 DbConnection conn = c.Database.Connection; - person Nuri YILMAZ; 17.05.2013
comment
также c.Database.SqlQuery и c.Database.ExecuteSqlCommand - person Mohamed Nuur; 28.09.2013

Да, ты можешь это сделать. Посмотрите на EntityConnection.StoreConnection. Вы можете получить соединение из ObjectContext.

person Craig Stuntz    schedule 16.10.2009
comment
Привет, Крейг. Я пробовал это раньше, и это не работает, потому что EntityConnection.CreateDbCommand создает EntityCommand, который принимает только Entity SQL. Но... ваше предложение заставило меня более внимательно изучить класс EntityConnection, который, как выяснилось, имеет свойство StoreConnection, которое я и ищу: подключение к фактической базовой СУБД. Спасибо, что указали мне правильное направление! - person Justin Grant; 16.10.2009
comment
Да все верно. Спасибо за исправление. Я обновлю свой ответ, чтобы включить то, что вы пишете. - person Craig Stuntz; 16.10.2009
comment
Кстати, я думаю, вы должны принять свой ответ вместо этого; это лучше, чем у меня. - person Craig Stuntz; 16.10.2009

Согласно этой публикации, нет поддержки DML в Entity Framework V1.

Зато есть поддержка хранимых процедур, но только таких из них, которые возвращают сущности. Это означает, что если вы создаете хранимую процедуру таким образом (синтаксис сервера sql):

CREATE PROCEDURE [dbo].[usp_trncate]
AS
BEGIN
 truncate table t1
END

Вы не можете импортировать его как функцию (на самом деле вы можете, но это не будет работать - код для этой функции не будет сгенерирован)

Я нашел костыльное решение для достижения цели: если вы определяете sp, как показано ниже:

CREATE PROCEDURE [dbo].[usp_trncate]
AS
BEGIN
 truncate table t1

 select top 1 * from t1
END

вы можете импортировать как функцию и использовать ее в своем коде следующим образом:

TestEntities context = new TestEntities();
context.TruncateTable();

(TruncateTable — это имя импортированной функции)

Я думаю, что есть другое (лучшее) решение. Другие способы копания - это возиться с файлом .edmx, чтобы сделать sp без возврата, или написать другой sp и сопоставить его с функцией вставки таблицы (эта sp должна усекать таблицу и вставлять строку)

Я надеюсь, что это поможет вам.

person bniwredyc    schedule 16.10.2009
comment
хорошая идея, но я не хочу менять схему БД только для того, чтобы обойти ограничение на стороне клиента. - person Justin Grant; 16.10.2009

Спасибо, Джастин. Я часами пытался понять, почему свойство StoreConnection недоступно в моем соединении с Entity Framework....

Ключевым моментом для меня был тот факт, что вы должны формально преобразовать свое соединение EF в System.Data.EntityClient.EntityConnection:

var newConn = (System.Data.EntityClient.EntityConnection)db.Connection;

Как только я это сделал, все остальное имело смысл.

person orderedanalog    schedule 05.11.2012