Creepin
Creepin

Reputation: 509

Abstract Azure IMobileServiceTable<T> behind repository

I want my repsository to be independent of the data access technology. Currently I am working on a Xamrin.Forms App that uses Azure Mobile App Services for data access. For performance and flexibility reasons I want my repository to look simmilar like the following:

Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)

Suppose my IDomainObject looks like the following:

public interface IDomainObject
{
    string Name { get; }
}

and my DataAccess Object:

internal class AzureDomainObject : IDomainObject
{
    public string Name { get; set; }
    public string Id { get; set; }
}

As far as I found out and tested I can do the following to query the database within my repository implementation:

public async Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
{
    // _table of type IMobileServiceTable<AzureDomainObject> gotten by MobileServiceClient
    var tableQuery = _table.GetQuery();
    tableQuery.Query = tableQuery.Query.Take(4); // 1) this was for testing and it works (ordering etc also works)
    // tableQuery.Query = query(tableQuery.Query); // 2) this was my initial idea how to use the input param
    await _table.ReadAsync(tableQuery);
}

My poblem now is how to use the input param query to replace 1) with 2).

tableQuery.Query expects an IQueryable<AzureDomainObject> but query is of type IQueryable<IDomainObject>.

Neither .Cast<AzureDomainObject>() nor .OfType<AzureDomainObject>() work to convert. Nor does (IQueryable<IAzureDomainObject>)query; work.

Cast and OfType throw NotSupportedException and the hard cast throws an InvalidCastException.

I also tried to extract the Expression from the query input param and assign it to the tableQuery.Query. But then a runtime exception occurs that they are not compatible.

Another idea I had was to use the ReadAsync(string) overload and pass the string representation of the passed query param. But this way I don't know how to generate the string.

So the final question is: Does anyone knows how to hide both AzureDomainObject and IMobileServiceTable from the domain model but keep the flexibility and performance benefits of IQueryable in the repository interface?

Upvotes: 0

Views: 62

Answers (1)

Bruce Chen
Bruce Chen

Reputation: 18465

According to your description, I checked this issue and here is my implementation for this scenario, you could refer to them.

Model:

public class TodoItem : IDomainObject
{
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }

    [JsonProperty(PropertyName = "complete")]
    public bool Complete { get; set; }
}

public interface IDomainObject
{
    string Id { get; set; }
}

Repository:

public interface IAzureCloudTableRepository<T> where T : IDomainObject
{
    Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query);
}


public class AzureCloudTableRepository<T> : IAzureCloudTableRepository<T> where T : IDomainObject
{
    IMobileServiceTable<T> table;

    public AzureCloudTableRepository(MobileServiceClient client)
    {
        this.table = client.GetTable<T>();
    }

    public async Task<T> CreateItemAsync(T item)
    {
        await table.InsertAsync(item);
        return item;
    }

    public async Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query)
    {
        var tableQuery = this.table.CreateQuery();
        tableQuery.Query = tableQuery.Query.Take(4); //the internal fixed query
        tableQuery.Query = query(tableQuery.Query);  //the external query
        return await tableQuery.ToEnumerableAsync();
    }
}

TEST:

var mobileService = new MobileServiceClient("https://{your-app-name}.azurewebsites.net");
var todoitem = new AzureCloudTableRepository<TodoItem>(mobileService);
var items = await todoitem.GetDomainObjectAsync((query) =>
{
    return query.Where(q => q.Text!=null);
});

enter image description here

Upvotes: 1

Related Questions