我正在使用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_属性一样使用?
答案 0 :(得分:3)
在Damien Guard的博客上有一个代码示例,展示了如何做到这一点(即在查询中使用客户端属性):
http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider
那就是说,我认为DateTime.ToUniversalTime无论如何都不会转换为SQL,因此无论如何你可能需要为UTC翻译编写一些数据库端逻辑。在这种情况下,将UTC日期/时间公开为计算列db-side并包含在L2S类中可能更容易。
E.g:
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
答案 1 :(得分: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 ) );
}
}
}
答案 2 :(得分:1)
另一个例子不能伤害我猜。 在我的Template类中,我有一个字段Seconds,我相对于UTC时间转换为TimeStamp。该声明还有一个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&gt; = 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);
每个组 - 模板组合需要定义最大事件数。
无论如何,这个技巧加快了查询速度四倍左右。