LINQ2SQL: как изменить значения полей при загрузке анонимных сущностей?

!!! Пожалуйста, не перенаправляйте на в этой статье, поскольку это не решает проблему, описанную ниже.

Допустим, у нас есть такая таблица в базе:

SomeTable

  • ID (int)
  • DT (datetime)

мы настроили контекст данных Linq2Sql. И мы настроили сущность для SomeTable:в onloaded метод изменяет DT таким образом, что DateTimeKind DT становится Utc (изначально он не указан).

теперь вот проблема:

Если мы запрашиваем данные с помощью целого объекта, метод OnLoaded называется:

From x In ourDataContext.SomeTable Select x

но если мы запрашиваем только часть таблицы (и, следовательно, генерируем анонимный тип), OnLoaded не вызывается:

From x In ourDataContext.SomeTable Select x.DT

ясно, что OnLoaded определяется в SomeTable сущности, а не в анонимном типе.

На данный момент я считаю создание пользовательские сущности, которые заменят анонимные типы. Но, может быть, у кого-то есть лучшее решение?

5 ответов


у нас была аналогичная проблема, поскольку нам нужно было получить часть полей от сущности как анонимный объект и всегда знать, что у нас есть DateTimeKind полей даты как DateTimeKind.UTC без использования дополнительных функций в запросе LINQ.

мы пробовали много чего, но нашли только одно достаточно хорошее решение - генерация кода для Linq2Sql с T4.

П. С. если вы хотите узнать больше о генерации кода Linq2Sql с помощью T4, вы можете начать с http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx


Linq2Sql генерирует частичные классы для таблиц, что упрощает расширение. Просто добавьте SomeTable.cs файл в ваше решение (в том же пространстве имен, что и автоматически созданный контекст БД) и определите дополнительное свойство с любым необходимым вам поведением:

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 обеспечивает некоторую гибкость здесь (как показано выше), у вас есть другие варианты, если решение не удовлетворяет:

  1. явный слой над L2S DAL. Обычно это шаблон репозитория это возвращает DTOs, очень похожие на те, которые автоматически генерируются L2S. В этом случае вы можете скрыть DT свойство заставляя потребителей использовать CustomDT только.
  2. поместите логику в базу данных (представления, вычисляемые столбцы, СПС). Я не считаю этот подход для нового проекта, но это может быть жизнеспособный вариант для некоторых устаревших приложений.

вы можете указать 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()

вы могли бы использовать linq-to-sql для части запроса и использовать linq-to-objects чтобы захватить DateTime свойство (вы не возвращает анонимный тип).

(From x In ourDataContext.SomeTable _
 Select x).AsEnumerable() _
          .Select(Function(x) x.DT)

вы можете попробовать этот код? Вместо анонимного типа можно указать тот же тип таблицы, но загрузить только одно поле. Я не знаю, сработает ли это в вашем случае.

SomeTable.Select( x => new SomeTable {
    DateField = x.DateField
})

в противном случае нет простого решения.