Entity Framework с агрегацией LINQ для конкатенации строки?
Это легко для меня выполнить в TSQL, но я просто сижу здесь, стуча себя в спину, пытаясь заставить его работать в EF4!
У меня есть таблица, давайте назовем ее TestData. Он имеет поля, например: DataTypeID, Name, DataValue.
DataTypeID, Name, DataValue
1,"Data 1","Value1"
1,"Data 1","Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"
Я хочу сгруппировать по DataID/Name и объединить DataValue в строку CSV. Желаемый результат должен содержать -
DataTypeID, Name, DataValues
1,"Data 1","Value1,Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"
Теперь, вот как я пытаюсь это сделать -
var query = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = (string)g.Aggregate("", (a, b) => (a != "" ? "," : "") + b.DataValue),
}).ToList()
Проблема в том, что LINQ to Entities не знает, как преобразовать это в SQL. Это часть объединения трех запросов LINQ, и мне бы очень хотелось, чтобы это было так. Я предполагаю, что я мог бы получить данные, а затем выполнить агрегат позже. По соображениям производительности это не сработает для моего приложения. Я также рассмотрел использование функции SQL-сервера. Но это просто не кажется "правильным" в мире EF4.
Кто-нибудь может взломать это?
Ответы
Ответ 1
Спасибо moi_meme за ответ. То, что я надеялся сделать, НЕ ВОЗМОЖНО с LINQ для Entities. Как и другие, вы должны использовать LINQ to Objects для получения доступа к методам манипулирования строкой.
См. ссылку, опубликованную moi_meme для получения дополнительной информации -
(Обновлено Ссылка, спасибо jnm2 за то, что дайте мне знать) http://www.purritos.com/blog/archives/4510
Ответ 2
Если ToList()
является частью вашего исходного запроса, а не просто добавлен для этого примера, используйте LINQ to Objects в результирующем списке, чтобы выполнить агрегацию:
var query = (from t in context.TestData
group t by new { DataTypeID = t.DataTypeID, Name = t.Name } into g
select new { DataTypeID = g.Key.DataTypeID, Name = g.Key.Name, Data = g.AsEnumerable()})
.ToList()
.Select (q => new { DataTypeID = q.DataTypeID, Name = q.Name, DataValues = q.Data.Aggregate ("", (acc, t) => (acc == "" ? "" : acc + ",") + t.DataValue) });
Протестировано в LINQPad, и он производит этот результат:
![alt text]()
Ответ 3
В некоторых ответах предлагается вызвать ToList(), а затем выполнить вычисление как LINQ to OBJECT. Thats штраф за небольшое количество данных, но если у меня есть огромное количество данных, что я не хочу загружать в память слишком рано, то ToList() может не быть вариантом.
У меня было аналогичное требование. Моя проблема состояла в том, чтобы получить список дочерних элементов сущности и создать строку значений, разделенных запятыми, с первым символом этого дочернего элемента.
-
Я создал свойство в моей модели просмотра, в котором будут храниться исходные данные из репозитория.
public class MyViewModel
{
public string AnotherRegularProperty { get; set; }
public IEnumerable<string> RawChildItems { get; set; }
public string FormattedData
{
get
{
if (this.RawChildItems == null)
return string.Empty;
string[] theItems = this.RawChildItems.ToArray();
return theItems.Length > 0
? string.Format("{0} ( {1} )", this.AnotherRegularProperty, String.Join(", ", theItems.Select(z => z.Substring(0, 1))))
: string.Empty;
}
}
}
Итак, моя ViewModel была готова. После этого я загрузил данные из LINQ в Entity в эту модель просмотра без вызова .ToList(), который загрузил бы все данные в память. Если в базе данных были thoudands записей, я бы никогда не вызывал .ToList().
Пример:
IQueryable<MyEntity> myEntities = _myRepository.GetData();
IQueryable<MyViewModel> viewModels = myEntities.Select(x => new MyViewModel() { AnotherRegularProperty = x.AProperty, RawChildItems = x.MyChildren })
Теперь я могу вызвать свойство FormattedData MyViewModel в любое время, когда мне нужно, и Getter будет выполняться только при вызове свойства.
Ответ 4
Может быть, неплохо создать представление для этого в базе данных (которое объединяет поля для вас), а затем заставить EF использовать это представление вместо исходной таблицы?
Я уверен, что это невозможно в инструкции LINQ или в параметрах Mapping.
Ответ 5
Ты уже очень близко. Попробуйте следующее:
var query = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = String.Join(",", g),
}).ToList()
В качестве альтернативы вы можете сделать это, если EF не разрешает String.Join
(который выполняется Linq-to-SQL):
var qs = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = g
}).ToArray();
var query = (from q in qs
select new
{
q.DataTypeID,
q.Name,
DataValues = String.Join(",", q.DataValues),
}).ToList();