3

Windows Phone ライブラリに Windows Azure Mobile Service を実装しようとしています。私はDataContract次のように定義しました:

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

次に、LINQ 式を使用して、次のようにテーブルからデータをロードしようとします (不要な詳細は削除されています)。

public async void SynchronizeAsync<TEntity>() where TEntity : ISyncableBase
{           
    var remoteTable = MobileServiceConnection.GetTable<TEntity>();

    //Get new entities
    var newEntities = await remoteTable.Where(item => item.RemoteLastUpdated > currentTimeStamp).ToListAsync();
}

ただし、最後の行に来るとすぐに、"The member 'RemoteLastUpdated' is not supported in the 'Where' Mobile Services query expression 'Convert(item).RemoteLastUpdated'." Same is the case for IsDeletedand evenというエラーが表示されますRemoteId。どうやら、私はWhere演算子を使用することはできません。不足しているものはありますか?

編集:この種の問題は解決しましたが、理由がわかりません! 解説参考になります。一般的な方法を次のように変更しました。

public async void SynchronizeAsync<TEntity>() where TEntity : class, ISyncableBase, new()

制約classnewジェネリックに追加したTEntityところ、正常に動作するようになりました。これがどのように起こったのか分かりますか?

4

3 に答える 3

2

classとのnew()制約を使用して、自分自身のブロックを解除できたようです。実際には最初のものだけが必要です-これはうまくいくはずです:

public async Task SynchronizeAsync<TEntity>()
    where TEntity : class, ISyncableBase { ... }

さて、なぜ一方が機能し、もう一方が機能しないのか、2 つのケースで生成される式が異なります。制約を使用するclassと、これは Where 句に対して生成される式です。

item => (item.RemoteLastUpdated > currentTimeStamp)

その制約がない場合は、次の式が作成されます。

item => (Convert(item).RemoteLastUpdated > currentTimeStamp)

これは、インターフェイス型に制約されている汎用オブジェクトが値型になる可能性があり、そのプロパティにアクセスできるようにオブジェクトとしてボックス化する必要があるためです。また、一般的な変換は、Azure Mobile Services クライアント SDK で使用される Linq から OData へのコンバーターではサポートされていません。

以下のコードは、式の 2 つのバージョンを示しており、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; }
}
于 2013-10-10T04:09:20.887 に答える
1

RemoteLastUpdated 列は TEntity クラスに存在しますか?

これを試して:

public async void SynchronizeAsync<ToDoCategory>()
{           
    var remoteTable = MobileServiceConnection.GetTable<ToDoCategory>();

    //Get new entities
    var newEntities = await remoteTable.Where(item => item.RemoteLastUpdated > currentTimeStamp).ToListAsync();
}

上記のコードが機能する場合は、"RemoteLastUpdated" を ISyncableBase から継承するクラスに移動し、この列をそこに配置する必要があります。その後、ToDoCategory クラスを変更して、この新しいクラスを継承します。

サンプル:

    [DataContract]
    public class MobEntity : NotifyBase
    {
        [Column]
        [DataMember]
        public DateTime RemoteLastUpdated { get; set; }
    }

[Table]
[DataContract]
public class ToDoCategory : MobEntity , 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; }
}

そして最後に:

   public async void SynchronizeAsync<MobEntity>() where MobEntity : ISyncableBase
    {           
        var remoteTable = MobileServiceConnection.GetTable<ToDoCategory>();

        //Get new entities
        var newEntities = await remoteTable.Where(item => item.RemoteLastUpdated > currentTimeStamp).ToListAsync();
    }
于 2013-10-09T11:38:55.860 に答える
0

まず、この場合ジェネリックを使用する必要はありません。

次に、正しいテーブル名を使用するため、Where 句が機能します。

var table = App.MobileService.GetTable<GameScore>();
var resultList = await table.Where(g => g.Score > 500).ToListAsync();
于 2013-10-09T13:51:16.953 に答える