New Posts New Posts RSS Feed: Repository suggestion
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Repository suggestion

 Post Reply Post Reply
Author
kdev View Drop Down
Groupie
Groupie
Avatar

Joined: 03-Jan-2013
Posts: 83
Post Options Post Options   Quote kdev Quote  Post ReplyReply Direct Link To This Post Topic: Repository suggestion
    Posted: 23-Jul-2013 at 4:23am
Hi,

In my Repositories, I have many Root Entities which load few RelatedEntityList. 
Right now I am overriding the GetKeyQuery and add many include to my base query. In some case this is becoming an issue as the query aggregate a LOT of data since all is done in one query.
Instead of using Includes, I would like to be able to execute some queries in order to load subproperties faster, and avoid EF generating a huge single SQL query with hundreds of fields.

Could it be possible to have this feature in cocktail ?

I had the same issue in one of my search repository and I did it like this :

public class VirtualSearchRepository<TResult> : IVirtualSearchRepository<TResult> 
        where TResult : class
{

        protected virtual IEntityQuery<TResult> DefineQuery()
        {
            return null;
        }
        
        protected virtual Task<IEntityQuery<TResult>> DefineQueryAsync()
        {
            var tcs = new TaskCompletionSource<IEntityQuery<TResult>>();
            
            tcs.SetResult(DefineQuery());

            return tcs.Task;
        }

        public async Task<IEnumerable<TResult>> AllAsync(Action<TQueryOptions> queryOptions = null, Func<IQueryable<TResult>, IOrderedQueryable<TResult>> orderBy = null, Action<IFetchOptions<TResult>> fetchOptions = null)
        {
            ...
            var qry = await DefineQueryAsync();
            ...

            return await qry.ExecuteAsync();
        }
}


that way, If I override the method "IEntityQuery<TResult> DefineQuery()" I am in the exact same situation as with cocktail,
I define my query and can add few include if this solution is fast enought for me.
But if having multiple query is faster, I just have to override the other method "Task<IEntityQuery<TResult>> DefineQueryAsync()"

exemple :

private async Task<Customer> GetCustomer(int keyValue)
{
            var taskCustomer = new EntityKey(typeof(Customer), keyValue).ToQuery().With(manager).ExecuteAsync();
            var taskOrders = new EntityQuery<Order>().With(manager)
                .Where(p => p.Customer_Id == keyValue)
                .ExecuteAsync();

            await TaskEx.WhenAll(taskCustomer, taskOrders);

            return (Customer)taskCustomer.Result.Cast<object>().ToList().First();
}

In my UnitOfWork I have to add this to not have the RelatedEntityList in the Pending mode:

Manager.MetadataStore.GetEntityMetadata(typeof(Customer))
                .NavigationProperties.First(p => p.Name.Equals(customer.EntityPropertyNames.Orders))
                .ReferenceStrategy = EntityReferenceStrategy.NoLoad;



If you think that this feature has nothing to do in Cocktail's code, could you at least define the following method as virtual in the Repository ?
public Task<T> WithIdAsync(object[] keyValues, CancellationToken cancellationToken)


regards



Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jul-2013 at 6:19am
Have you thought about using projections? Here is a snippet of what we do in one of our search screens


        public static IQueryable<AccountListItem> ToAccountListItemQuery(this IQueryable<Model.Account> query)
        {
            return query.Select(a => new AccountListItem
            {
                AccountId = a.AccountId,
                AccountNumber = a.AccountNumber,
                PrimaryOwner = a.AccountOwners.Where(ao => ao.IsPrimary).Select(ao => ao.Owner).FirstOrDefault(),
                PrimaryOwnerAddress = a.AccountOwners.Where(ao => ao.IsPrimary).SelectMany(ao => ao.Owner.OwnerAddresses).Where(oa => oa.IsPrimary).Select(oa => oa.Address).FirstOrDefault(),
                StreetNumber = a.StreetNumber,
                StreetName = a.StreetName,
                City = a.City,
                PostalCode = a.PostalCode
            });
        }


I take several properties on the account and a few of the related entities.

I call the FindAsync method on the repository with my selector (above method) and then I can pass my filter and order by clauses. If you'd like I'd be happy to post some code samples.

In some cases we have to actually calculate balances, so we actually have a remote method on the server that runs the queries, calculates anything it needs to and returns just the projection to the client.
Back to Top
kdev View Drop Down
Groupie
Groupie
Avatar

Joined: 03-Jan-2013
Posts: 83
Post Options Post Options   Quote kdev Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jul-2013 at 6:27am
hi smi-mark,

Of course I already iuse the projection when I need to show a selection list.

My point was more about the edition of an entity (you can't use projection there). When you follow the pattern used in temphire, you create a specific repository for the root entity when you want to edit it. This repository have to include all the related entity you might be able to modify on your screen. This is done with the method Include() which is well known to be slow with EF.
So instead of doing many include, I would prefer to have x query running in parallel which should be in some case faster.


Regards,
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jul-2013 at 7:59am
Oh, ok. I was confused thinking you were talking about search.

In our AccountRepository we use includes for some of the basic information needed.

in our Account screen we have multiple sections and we load extra data in either the OnActivate method, when the screen is first used, or the OnInitialize method so it loads at the start. This has helped our application response time a lot
Back to Top
kdev View Drop Down
Groupie
Groupie
Avatar

Joined: 03-Jan-2013
Posts: 83
Post Options Post Options   Quote kdev Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jul-2013 at 8:19am
Yes that is another possibilities but if I break my load and only load some navigation properties then when I active a specific part of my screen (using a tabcontrol for exemple) then my validate method located on the root entity might not work as expected.
It won't know which related entitied been loaded or not and so what it have to validate (and I don't talk about the interceptors using some of those navigations properties).





Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jul-2013 at 8:39am
If your validate is simply on save you could do something like:

If your save method is called "Save" you could modify your "CanSave" property to be based on if all the data is loaded or not
Load the main data first
Start your parallel queries or however you load the rest and then set the loaded property.

I would assume it's unlikely that a user could come in, make a change and want to save before the data is all loaded, but this way the save button gets enabled once the data is loaded, and then all your validation logic should work.

Just some thoughts!
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jul-2013 at 12:33pm
Mark provided a very good list of suggestions. The use of Include should be minimized for some of the very reasons that have already been stated. However, I don't think there's a need to change anything in Cocktail. It is the responsibility of your particular ViewModel to orchestrate the loading of the data and enable/disable the appropriate UI controls to prevent the user from performing an operation before the data is ready. 

Only you as a developer know which load operations can be performed in parallel vs which ones need to be sequential in order to achieve the best possible performance. It is a good idea to add services to your UoW that encapsulate the more complex load operations.

Mark made a good point, that the save button should be disabled until all the required data has been loaded before allowing the user to perform a save. This can be done very easily if you encapsulate the logic in an asynchronous service method. 
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down