This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Hey guys, for a project I'm working on, I'm trying to be pretty disciplined about separating commands and queries into separate services. Commands, which will sometimes result in DB updates will be done via a pub/sub mechanism. This will use full Lightspeed capabilities to perform CRUD operations. Retrieving data to the UI will be done via query services which are separate. I'd like to be able to leverage the same LS domain model used in the command area for many of the queries, but i'm worried about unnecessary LS overhead. Are there optimizations you would recommend for this? For example, is there an easy way to disable change tracking if there will never be updates and the model is just used as a query mechanism? Any thoughts appreciated. THanks! Eric |
|
|
I can't think of anything that would be significant. Change tracking is only turned on if you ask for it, so that shouldn't be an issue. Validation only runs when you ask for it or save the entity, so that shouldn't be an issue either. Probably the main thing you can do is to be smart about your use of eager loading and named aggregates, but these aren't specific to a read-only scenario. |
|
|
Ivan, thanks for the reply. You mentioned: "Change tracking is only turned on if you ask for it". How is that configured? Also, here is an example of my concern. I noticed that when performing a FindAll for my "Roles" table, which is set up for L2 cache, that it attempts to set all Roles in my results to the L2 cache. I would expect that a query would simply map the values from the database and skip any attempt to set cache values unless I specifically accessed one of the roles individually. Now, if a load occurred, including a lazy load or eager load, I would expect the objects to be set in the L2 cache. But my concern would be that this is not really necessary for the query and will degrade it's performance if I am returning many results. I also tried this with a Queryable.ToList() with the same results. If I run the query 10 times it sets the L2 cache each time. I guess one method would be to turn off caching on the query side, but I could see some potential benefits if I'm converting the results to a list of DTO's, taking advantage of lazy loading child collections etc... What are your thoughts on this? Thanks, Eric |
|
|
To enable change tracking, set the entity.ChangeTracker.TrackingMode. The default is none, meaning no change tracking. I'm not very familiar with the caching stuff -- I'll ask JD or Jeremy to get back to you on this. |
|
|
Hi Eric, Not sure I fully understand the context on this, but if you are using the L2 cache then it will cache items in two ways. If you are executing a query which performs a find by identifier, then both the L1 (IdentityMap) and L2 cache will be checked and a value returned if it is found. As an entity is loaded from the database, if the entity type is set to be cached it will be set in the L2 cache. If you are executing a query which performs a find all, then the L2 cache will be checked to see if a collection for the entity type is set and if it is it will be returned. As a collection is loaded from the database, if the entity type is set to be cached then that collection will be set in the L2 cache. So if you are opting in to caching, this is definitely the behavior you will want. If you dont want the caching then simply dont enable a cache provider on the context you are using to create the UnitOfWork, or at a more global level turn off caching on that entity type. Hope that provides some insight into how things are working; Fire back any questions on this if you need some more help solving things more specifically for what you are building though :)
Jeremy |
|
|
Thanks for the answer Jeremy. I was surprised that the findall added all instances to the cache each time I used it (calling SetItem on every call), even if items were already cached. Also when I did Queryable.ToList() with or without criteria I saw the same behavior. I am concerned that every query would result in setting the cache for each instance returned. I get it for a findall, but for a queryable, that's used in so many places, I'd be worried about the performance of all those sets. Jeremy, you also mentioned that if a FindAll was performed, that the L2 cache would be checked. However, I didn't see this behavior. I implemented my own cache provider for AppFabric (Velocity), and put breaks on the getItem and setItem. I only see an L2 cache hit when I specifically call a FindById, which makes sense. Is there something I'm missing? On a related note, a really cool feature would be the capability to disable caching for a specific UOW instance. I want to reuse the model across a couple of projects, so I would want caching on the specific entities that I think would benefit me the most, and I'm registering the Context as a singleton for most of the apps, so I wouldn't want to turn off caching there since i would lose all caching from that context, but it would be nice to have the capability to turn on or off caching in specific situations. NHib has this capability and it really comes in handy for fine tuning situations. Query caching would also be nice btw :-). Any advice on how I could implement that? The main issue I see is that I'm doing a lot of querying by exposing a queryable from my base repository and using extension methods to apply filters as needed. I'm not sure of a way to inject something before the execution of the query to check the cache first since I can't predict when the deferred execution will happen. Not sure I can do that without some hook into the mechanism. Any ideas? Thanks, Eric
|
|
|
The code for checking and setting the cache (GetCollection, SetCollection) is in the same method so it does seem a bit strange that it would be setting it in the cache repeatedly unless the cache was actually missing in each case. If you havnt set an explicit expiry time then the default of 15 minutes is used. You mentioned you are calling IQueryable.ToList(), unless the IQueryable is actually a FindAll - e.g. UnitOfWork.MyEntity.ToList() it should not be involved in any caching anyway since we dont do query based caching. Are you actually seeing the individual entities being set in the cache rather than the collection? Because caching is held on the context its not possible to disable it on a per UnitOfWork basis currently, although if you only ever have a single UnitOfWork active you could disable it on the context and then reenable it as required. Ill have a think about how we could accomodate this though. With the query caching, Im not sure it would be possible to do this manually if I am understanding you correctly. If you are using IQueryables then LightSpeed is always going to be involved in the execution of the query so the caching would need to be handled by LightSpeed. Normally you could use an approach of checking a cache key prior to worrying about constructing and executing a query - e.g. if you are using a Repository which returns lists of materialized entities you could just construct a key based on the query parameters and then cache the results based on that.
Jeremy |
|
|
Hi Jeremy, Yeah, I understand on the query caching. I'll mostly be querying at a service level and caching results at the consumer level, but it's nice to have the option to cache queries at the repository if possible. On the setCache, both when I do a Queryable.ToList (via lightspeed's Uow.Query<T>.ToList() and a FindAll (using Find<T>), I'm seeing individual items cached, not collections. Example findall and queryable calls on my repository that cause setItem calls (my "Queryable" wraps the lightspeed Query<T> FindAll wraps Find<T>):
IList<Role
> roles = _roleRepository.FindAll();
IList<Role> roles = _roleRepository.Queryable.Where(x => x.SearchName == "Admin").ToList(); I have not yet seen a collection get cached. For the queryable, even when I added a when clause, it was calling the setItem method for each item returned. I'm using LS 4, so not sure if that might have a defect here. Anyway, I can send you a sample if you like, but I'm using the Appfabric cache, so you'd have to set this up or switch to a different cache mechanism. I've implemented the ICache interface. Is that the only interface I need to implement? Thanks, Eric
|
|
|
Right, that makes more sense then. The entities will be being cached simply because they are being loaded which will occur in any query but they will only be fetched from cache if calling FindById. Have you set the CacheFindAllResult property on your Cache attribute for the entity type? (see: http://www.mindscapehq.com/forums/Post.aspx?ThreadID=3745&PostID=12910 for details). If not then that would explain why there is no collection caching occuring.
Jeremy |
|
|
Cool, thanks for that info. One other question. I understand why a generic linq query would materialize each entry when "ToList" was called, thus causing an individual call to set cache for each. Do you have any recommendations on a method of query to avoid this? Thanks, Eric |
|
|
For the caching? No, if you have enabled caching then currently that is the intended behavior. There isnt any query overload to disable caching or change the behavior of how the caching operates.
Jeremy |
|