Entity Framework 4.1: невозможно выполнить преобразование из DbQuery в ObjectQuery
У меня есть следующий код:
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]'.
Может кто-нибудь дать какой-либо намек на это? Спасибо!
EDIT: Первое решение 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...
Ответы
Ответ 1
Это потому, что ваш _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
- просто еще один пример упрощения в DbContext API, который затрудняет его использование.
Ответ 2
В моем случае мне действительно нужны параметры, и я нашел это обходное решение:
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.: -)