我看到您可以通过使用class
andnew()
约束来解锁自己。你实际上只需要第一个 - 这应该工作:
public async Task SynchronizeAsync<TEntity>()
where TEntity : class, ISyncableBase { ... }
现在,为什么一个有效而另一个无效,为这两种情况生成的表达式是不同的。有了class
约束,这是为您的 Where 子句生成的表达式:
item => (item.RemoteLastUpdated > currentTimeStamp)
而如果你没有那个约束,这是创建的表达式:
item => (Convert(item).RemoteLastUpdated > currentTimeStamp)
这是因为一个被约束为接口类型的泛型对象可能是一个值类型,并且它需要被装箱为一个对象,以便我们可以访问它的属性。Azure 移动服务客户端 SDK 使用的 Linq-to-OData 转换器通常不支持转换。
下面的代码显示了表达式的两个版本,并且具有仅适用于class
约束的通用方法的版本。
public sealed partial class MainPage : Page
{
public static MobileServiceClient MobileService = new MobileServiceClient("appUrl", "appKey");
public MainPage()
{
this.InitializeComponent();
}
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
AddToDebug("No class constraint:");
NoClassConstraint<ToDoCategory>();
AddToDebug("With class constraint:");
WithClassConstraint<ToDoCategory>();
//await SynchronizeAsync<ToDoCategory>();
}
private void NoClassConstraint<TEntity>() where TEntity : ISyncableBase
{
DumpWhereExpression<TEntity>(item => item.RemoteLastUpdated > DateTime.Now);
}
private void WithClassConstraint<TEntity>() where TEntity : class, ISyncableBase
{
DumpWhereExpression<TEntity>(item => item.RemoteLastUpdated > DateTime.Now);
}
public async Task SynchronizeAsync<TEntity>() where TEntity : class, ISyncableBase
{
try
{
var remoteTable = MobileService.GetTable<TEntity>();
DateTime currentTimeStamp = DateTime.UtcNow.Date;
//Get new entities
var newEntities = await remoteTable.Where(item => ((ISyncableBase)item).RemoteLastUpdated > currentTimeStamp).ToListAsync();
AddToDebug("New entities: {0}", string.Join(" - ", newEntities.Select(e => e.RemoteLastUpdated)));
}
catch (Exception ex)
{
AddToDebug("Error: {0}", ex);
}
}
private void DumpWhereExpression<T>(Expression<Func<T, bool>> predicate)
{
AddToDebug("Predicate: {0}", predicate);
}
void AddToDebug(string text, params object[] args)
{
if (args != null && args.Length > 0) text = string.Format(text, args);
this.txtDebug.Text = this.txtDebug.Text + text + Environment.NewLine;
}
}
public interface ISyncableBase
{
DateTime RemoteLastUpdated { get; set; }
}
[DataContract]
public class NotifyBase { }
public class TableAttribute : Attribute { }
public class ColumnAttribute : Attribute
{
public string DbType { get; set; }
public bool IsDbGenerated { get; set; }
public bool IsPrimaryKey { get; set; }
public bool CanBeNull { get; set; }
}
[Table]
[DataContract]
public class ToDoCategory : NotifyBase, ISyncableBase
{
[IgnoreDataMember]
[Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)]
public int LocalId { get; set; }
[Column(CanBeNull = true)]
[DataMember(Name = "id")]
public int RemoteId { get; set; }
[Column]
[DataMember]
public bool IsDeleted { get; set; }
[Column]
[DataMember]
public DateTime RemoteLastUpdated { get; set; }
[Column(CanBeNull = true)]
[DataMember]
public DateTime? LocalLastUpdated { get; set; }
}