Свойства псевдонима в LINQ to SQL - PullRequest
2 голосов
/ 10 сентября 2009

Я использую свойство shim, чтобы убедиться, что дата всегда в формате UTC. Само по себе это довольно просто, но теперь я хочу запросить данные. Я не хочу раскрывать базовое свойство, вместо этого я хочу, чтобы запросы использовали свойство shim. То, с чем у меня проблемы, это отображение свойства шим. Например:

public partial class Activity
{
    public DateTime Started
    {
        // Started_ is defined in the DBML file
        get{ return Started_.ToUniversalTime(); }
        set{ Started_ = value.ToUniversalTime(); }
    }
}


var activities = from a in Repository.Of<Activity>()
                 where a.Started > DateTime.UtcNow.AddHours( - 3 )
                 select a;

Попытка выполнить запрос приводит к исключению:

System.NotSupportedException: The member 'Activity.Started' has no supported 
translation to SQL.

Это имеет смысл - как LINQ to SQL может знать, как обрабатывать свойство Started - это не столбец или ассоциация? Но я искал что-то вроде атрибута ColumnAliasAttribute, который указывает SQL обрабатывать свойства Started как Started_ (с подчеркиванием).

Есть ли способ помочь LINQ to SQL преобразовать дерево выражений в свойство Started, можно использовать так же, как свойство Started_?

Ответы [ 3 ]

3 голосов
/ 10 сентября 2009

В блоге Damien Guard есть пример кода, показывающий, как это сделать (т.е. использовать клиентские свойства в запросах):

http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider

Тем не менее, я не думаю, что DateTime.ToUniversalTime все равно будет переводиться в SQL, поэтому вам, возможно, придется написать какую-то логику на стороне базы данных для переводов UTC. В этом случае может быть проще представить дату / время UTC как вычисляемый столбец на стороне базы данных и включить его в свои классы L2S.

например:.

create table utc_test (utc_test_id int not null identity,
  local_time datetime not null,
  utc_offset_minutes int not null,
  utc_time as dateadd(minute, 0-utc_offset_minutes, local_time),
  constraint pk_utc_test primary key (utc_test_id));  

insert into utc_test (local_time, utc_offset_minutes) values ('2009-09-10 09:34', 420); 
insert into utc_test (local_time, utc_offset_minutes) values ('2009-09-09 22:34', -240);   

select * from utc_test
2 голосов
/ 10 сентября 2009

На основе ответа @ KrstoferA я нашел надежное решение, которое скрывает тот факт, что свойства являются псевдонимами из клиентского кода. Поскольку я использую шаблон репозитория, возвращающий IQueryable [T] для определенных таблиц, я могу просто обернуть результат IQueryable [T], предоставленный базовым контекстом данных, и затем перевести выражение, прежде чем базовый поставщик скомпилирует его.

Вот код:

public class TranslationQueryWrapper<T> : IQueryable<T>
{
    private readonly IQueryable<T> _source;

    public TranslationQueryWrapper( IQueryable<T> source )
    {
        if( source == null ) throw new ArgumentNullException( "source" );
        _source = source;
    }

    // Basic composition, forwards to wrapped source.
    public Expression Expression { get { return _source.Expression; } }
    public Type ElementType { get { return _source.ElementType; } }
    public IEnumerator<T> GetEnumerator() { return _source.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

    // Intercept calls to the provider so we can translate first.
    public IQueryProvider Provider 
    { 
        get { return new WrappedQueryProvider(_source.Provider); } 
    }

    // Another wrapper around the provider
    private class WrappedQueryProvider : IQueryProvider
    {
        private readonly IQueryProvider _provider;

        public WrappedQueryProvider( IQueryProvider provider ) { 
            _provider = provider; 
        }

        // More composition
        public object Execute( Expression expression ) { 
            return Execute( expression ); }
        public TResult Execute<TResult>( Expression expression ) { 
            return _provider.Execute<TResult>( expression ); }
        public IQueryable CreateQuery( Expression expression ) { 
            return CreateQuery( expression ); }

        // Magic happens here
        public IQueryable<TElement> CreateQuery<TElement>( 
            Expression expression ) 
        { 
            return _provider
                .CreateQuery<TElement>( 
                    ExpressiveExtensions.WithTranslations( expression ) ); 
        }
    }
}
1 голос
/ 20 декабря 2010

Другой пример не может повредить, я думаю. В моем классе Template у меня есть поле Seconds, которое я конвертирую в TimeStamp относительно времени UTC. Это утверждение также имеет CASE (a? B: c).

    private static readonly CompiledExpression<Template, DateTime> TimeStampExpression =
        DefaultTranslationOf<Template>.Property(e => e.TimeStamp).Is(template => 
            (template.StartPeriod == (int)StartPeriodEnum.Sliding) ? DateTime.UtcNow.AddSeconds(-template.Seconds ?? 0) :
            (template.StartPeriod == (int)StartPeriodEnum.Today) ? DateTime.UtcNow.Date :
            (template.StartPeriod == (int)StartPeriodEnum.ThisWeek) ? DateTime.UtcNow.Date.AddDays(-(int)DateTime.UtcNow.DayOfWeek) :   // Sunday = 0
            (template.StartPeriod == (int)StartPeriodEnum.ThisMonth) ? new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, DateTimeKind.Utc) :
            (template.StartPeriod == (int)StartPeriodEnum.ThisYear) ? new DateTime(DateTime.UtcNow.Year, 1, 1, 0, 0, 0, DateTimeKind.Utc) :
            DateTime.UtcNow     // no matches
        );

    public DateTime TimeStamp
    {
        get { return TimeStampExpression.Evaluate(this); }
    }

Мой запрос на инициализацию таблицы истории на основе (Event.TimeStamp> = Template.TimeStamp):

    foreach (var vgh in (from template in Templates
                        from machineGroup in MachineGroups
                        let q = (from event in Events
                                 join vg in MachineGroupings on event.MachineId equals vg.MachineId
                                 where vg.MachineGroupId == machineGroup.MachineGroupId
                                 where event.TimeStamp >= template.TimeStamp
                                 orderby (template.Highest ? event.Amount : event.EventId) descending
                                 select _makeMachineGroupHistory(event.EventId, template.TemplateId, machineGroup.MachineGroupId))
                        select q.Take(template.MaxResults)).WithTranslations())
        MachineGroupHistories.InsertAllOnSubmit(vgh);

Требуется определенное максимальное количество событий на комбинацию шаблонов групп.

В любом случае, этот трюк ускорил запрос примерно в четыре раза.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...