Entity Framework 4.1: невозможно преобразовать из DbQuery в ObjectQuery - PullRequest
18 голосов
/ 26 августа 2011

У меня есть следующий код:

public void DeleteAccountsForMonth(int year, int month)
{
    var result = from acm in this._database.AccountsOnMonth
                 where ((acm.Year == year) && (acm.Month == month))
                 select acm.Id;
    var query = (ObjectQuery<int>)result;

    string sql = string.Format(
        "DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
        query.ToTraceString()
    );

    var parameters = new List<System.Data.SqlClient.SqlParameter>();
    foreach (ObjectParameter parameter in query.Parameters)
    {
        parameters.Add(new System.Data.SqlClient.SqlParameter {
            ParameterName = parameter.Name,
            Value = parameter.Value
        });
    }

    this._database.Database.ExecuteSqlCommand(sql, parameters.ToArray());
}

По сути, я пытаюсь удалить большую часть данных из контекста (получить результат запроса, получить SQL и выполнить его). Но у меня проблема при приведении result к ObjectQuery. Исключение, которое дает

Невозможно привести объект типа 'System.Data.Entity.Infrastructure.DbQuery 1[System.Int32]' to type 'System.Data.Objects.ObjectQuery 1 [System.Int32]'.

Кто-нибудь может дать подсказку, чтобы решить это? Спасибо!

РЕДАКТИРОВАТЬ: Первое решение Ladislav помогло мне решить проблему, но возникла небольшая проблема с параметрами SQL сгенерированного запроса SQL, то есть запрос SQL, сгенерированный query.ToString(), был таким:

DELETE FROM [SncAccountOnMonths] WHERE [SncAccountOnMonths].[Id] IN (
    SELECT [Extent1].[Id] AS [Id]
    FROM [dbo].[SncAccountOnMonths] AS [Extent1]
    WHERE ([Extent1].[Year] = @p__linq__0) AND ([Extent1].[Month] = @p__linq__1))

Проблема заключалась в том, что переменные @p__linq__0 и @p__linq__1 там, где они не объявлены, и поэтому запрос выдал ошибку «Необходимо объявить скалярную переменную @ p_ linq _0» (я уверен, что это даст та же ошибка для переменной @p__linq__1). Чтобы «объявить» их, мне нужно передать их в качестве аргументов ExecuteSqlCommand(). Итак, окончательное решение для первоначального ответа - код ниже:

public void DeleteAccountsForMonth(int year, int month)
{
    var result = (this._database.AccountsOnMonth
        .Where(acm => (acm.Year == year) && (acm.Month == month)))
        .Select(acm => acm.Id);
    var query = (DbQuery<int>)result;

    string sql = string.Format(
        "DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
        query.ToString()
    );

    this._database.Database.ExecuteSqlCommand(sql,
        new SqlParameter("p__linq__0", year),
        new SqlParameter("p__linq__1", month)
    );
}

Кстати, я предполагаю, что сгенерированные переменные всегда имеют формат @p__linq__, если только Microsoft Entity Framework Team не изменит его в любом будущем обновлении EF ...

Ответы [ 2 ]

23 голосов
/ 27 августа 2011

Это потому, что ваш _database является производным от DbContext, а ваш AccountsOfMonth равен DbSet<>. В таком случае вы не можете использовать ObjectQuery напрямую, потому что DbSet<> производит DbQuery<>, который не конвертируется в ObjectQuery<>.

Вы должны использовать DbQuery<> напрямую:

var result = from acm in this._database.AccountsOnMonth
             where ((acm.Year == year) && (acm.Month == month))
             select acm.Id;
var query = (DbQuery<int>)result;

string sql = string.Format(
    "DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
    query.ToString()
);

Или вы должны сначала преобразовать свой контекст в ObjectContext и создать ObjectSet<>:

var objectContext = ((IObjectContextAdapter)_database).ObjectContext;
var set = objectContext.CreateObjectSet<AccountsOnMonth>();
var resut = from acm in set
            where ((acm.Year == year) && (acm.Month == month))
            select acm.Id;

Проблема с первым подходом заключается в том, что DbQuery не предлагает Parameters collection - просто еще один пример упрощения в API DbContext, который только усложняет его использование.

1 голос
/ 09 сентября 2014

В моем случае мне действительно нужны параметры, и я нашел этот обходной путь:

var query = (DbQuery<int>)result;

FieldInfo internalQueryField = query.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(f => f.Name.Equals("_internalQuery")).FirstOrDefault();
var internalQuery = internalQueryField.GetValue(query);
FieldInfo objectQueryField = internalQuery.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(f => f.Name.Equals("_objectQuery")).FirstOrDefault();
ObjectQuery<int> objectQuery = objectQueryField.GetValue(internalQuery) as ObjectQuery<int>;

foreach (ObjectParameter objectParam in objectQuery.Parameters)
{
    SqlParameter sqlParam = new SqlParameter(objectParam.Name, objectParam.Value);
    // Etc...
}

Я реализую SqlCacheDependency с Entity Framework. : -)

...