2009-09-10 86 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); 

它需要每组模板组合事件的定义的最大数目。

无论如何,这个窍门加快了查询四次左右。