2009-09-10 84 views
2

我正在使用shim屬性來確保日期始終是UTC。這本身很簡單,但現在我想查詢數據。我不想公開底層屬性,而是希望查詢使用SHIM屬性。我遇到的麻煩是映射墊片屬性。例如:LINQ to SQL中的別名屬性

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到SQL怎麼會知道如何對待入門屬性 - 它不是一個列或協會?但是,我正在尋找像ColumnAliasAttribute這樣的東西,它告訴SQL將Started的屬性視爲Started_(帶下劃線)。

有沒有一種方法可以幫助LINQ to SQL將表達式樹轉換爲Started屬性可以像Started_屬性一樣使用?

回答

3

有顯示怎麼辦(在查詢中即使用客戶端屬性),關於達衛隊的博客代碼示例:

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

這麼說,我不認爲DateTime.ToUniversalTime將轉化無論如何,所以你可能需要爲UTC翻譯編寫一些數據庫端邏輯。在這種情況下,將UTC日期/時間公開爲計算的列db側並將其包含在您的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 
+0

這幾乎可行。問題在於它需要.WithTranslations處理LINQ語句 - 即使使用...自動語句。在這一點上,它只是直接引用Started_屬性。我真的很希望這個屬性能夠被調用代碼透明化。 – 2009-09-10 02:39:43

+0

如果數據庫結構沒有被鎖定(即屬於別人或某個無法更改的應用程序),我建議去選項#2 - 在數據庫中存儲爲UTC,或者存儲爲本地+偏移量在計算列中計算UTC。這有額外的好處,保護你從一個不同的時區比您所在的時區在當地時間做ToUniversalTime ... – KristoferA 2009-09-10 02:45:00

2

基於@KrstoferA's答案,我想出了隱藏的屬性從客戶端代碼混淆事實的可靠解決方案。由於我使用的庫模式爲特定的表返回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)); 
     } 
    } 
} 
+0

我認爲這可能是我正在尋找...但是...我不是100%肯定我明白你如何使用這個。提供示例的任何機會? – CodeRedick 2012-08-28 02:58:44

1

又如不能傷害我猜。 在我的Template類中,我有一個字段Seconds,我將它轉換爲相對於UTC時間的TimeStamp。這個陳述也有一個CASE(a?b:c)。基於(Event.TimeStamp> = Template.TimeStamp)

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); } 
    } 

我的查詢初始化歷史表:

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); 

它需要每組模板組合事件的定義的最大數目。

無論如何,這個竅門加快了查詢四次左右。