LINQ2SQL: как изменить значения полей при загрузке анонимных объектов?
!!! Пожалуйста, не перенаправляйте эту статью, так как она не решает проблему, описанную ниже.
Скажем, у нас есть такая таблица в базе данных:
SomeTable
Мы настроили контекст данных Linq2Sql. И мы настроили объект для SomeTable: Метод OnLoaded изменяет DT таким образом, что DateTimeKind DT становится Utc (изначально это Unspecified).
Теперь вот проблема:
Если мы запрашиваем данные с использованием целой сущности, вызывается метод OnLoaded:
From x In ourDataContext.SomeTable Select x
Но если мы запрашиваем только часть таблицы (и, следовательно, генерируем анонимный тип), OnLoaded не вызывается:
From x In ourDataContext.SomeTable Select x.DT
Понятно, что OnLoaded определен в сущности SomeTable, а не в анонимном типе.
В настоящий момент я рассматриваю создание настраиваемых объектов, которые заменяют анонимные типы. Но может быть, у кого-то есть лучшее решение?
Ответы
Ответ 1
У нас была аналогичная проблема, так как нам нужно было получать часть полей из объекта как анонимного объекта и всегда знать, что мы имеем DateTimeKind
полей даты как DateTimeKind.UTC
без использования дополнительных функций в запросе LINQ.
Мы пробовали много вещей, но мы нашли только одно достаточно хорошее решение - генерация кода для Linq2Sql с T4.
P.S. Если вы хотите узнать больше о генерации кода Linq2Sql с помощью T4, вы можете начать с http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx
Ответ 2
Linq2Sql генерирует частичные классы для таблиц, что упрощает их расширение. Просто добавьте файл SomeTable.cs
к вашему решению (в пределах того же пространства имен, что и ваш автоматически сгенерированный контекст db) и определите дополнительное свойство с любым поведением, которое вам нужно:
public partial class SomeTable {
public System.DateTime CustomDT {
get { return DT.AddYears(120); }
}
}
Теперь вы можете запросить его, как обычно:
var e = ctx.SomeTable.Select(x => new { x.CustomDT }).First();
Console.WriteLine(e.CustomDT);
Обновление
Основываясь на комментариях, я думаю, что проблема, с которой вы столкнулись, связана с неправильным разделением обязанностей. Вы пытаетесь передать бизнес-логику (преобразование данных) на ваш DAL. Хотя L2S обеспечивает некоторую гибкость здесь (как показано выше), у вас есть другие варианты, если решение не удовлетворяет:
- Явный уровень выше L2S DAL. Обычно это шаблон хранилища
который возвращает DTO, очень похожие на автогенерируемые L2S. В этом случае вы можете скрыть свойство
DT
, заставляющее потребителей использовать только CustomDT
.
- Поместите логику в базу данных (представления, вычисленные столбцы, SP). я
не будет рассматривать этот подход для нового проекта, но это может быть
жизнеспособный вариант для некоторых устаревших приложений.
Ответ 3
Вы можете указать DateTimeKind
в запросе:
from x in ourDataContext.SomeTable
select DateTime.SpecifyKind(x.DT, DateTimeKind.Utc)
Если вы будете делать это часто, метод расширения может помочь сделать его менее подробным:
public static class Ext
{
public static DateTime AsUtc(this DateTime dateTime)
{
return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
}
public static DateTime? AsUtc(this DateTime? dateTime)
{
if(dateTime == null) return null;
return AsUtc(dateTime.Value);
}
}
Затем ваш запрос будет выглядеть следующим образом:
from x in ourDataContext.SomeTable select x.DT.AsUtc()
Ответ 4
Вы можете использовать linq-to-sql
для части запроса и использовать linq-to-objects
для захвата требуемого свойства DateTime
(вы фактически не возвращаете анонимный тип).
(From x In ourDataContext.SomeTable _
Select x).AsEnumerable() _
.Select(Function(x) x.DT)
Ответ 5
Можете ли вы попробовать этот код? Вместо использования анонимного типа вы можете указать один и тот же тип таблицы, но загружать только одно поле. Я не знаю, будет ли это работать или нет в вашем случае.
SomeTable.Select( x => new SomeTable {
DateField = x.DateField
})
В противном случае нет простого решения.