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
|
Normal
0
21
false
false
false
SV
X-NONE
X-NONE
MicrosoftInternetExplorer4
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Normal tabell";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman","serif";}
Hi
I have a question regarding the way the database connection pool is used for UnitOfWork. As I understand it a new DB connection is opened when a UnitOfWork is created, and closed when it is disposed. Thus when retrieving data from the database lazy-loaded, the UnitOfWork has to be alive and kicking (i.e. not disposed) when referenced objects are accessed the first time since a database request can be made to the database for the data. Something like this for instance: .... However, what is the way to go when using a web application with a natural high load? We are using an MVP pattern and it is natural for objects to be retrieved in Presenters and later on used in Views. I can see from the documentation that you provide on PerRequestUnitOfWorkScope in LightSpeed. I can also see the usage in the MVC example application. Now to my fundamental question. When not using an O/R mapper I am used to always closing the database connection and instead letting the Threadpool control the opened connections. This minimizes the usage of the available opened connections. But it seems to me that by using the PerRequestUnitOfWorkScope, the connection will be opened throughout the request. And even worse, the connection would remain opened between requests. With a Threadpool default size maximum of 100, which I think is quite normal, this would call for a maximum of concurrent 100 users (concurrent with regards to session based) which is a quite small number. Am I missing something obvious or is there a way to achieve the UnitOfwork to have an opened connection only when actuall requests are being made to the database? (even if it occurs when lazy loading data). |
|
|
Oups, sorry for the Style Definition thingies in the start. Where on earth did that come from? |
|
|
Sorry again...Just realised something. Naturally you would always close the connection (as you do in the example implementation in the ASP.NET MVC SomeController overriden method OnResultExecuted) at the end of the requests. This would allow for substantially more than ~100 users pers session and instead a maximum of 100 actual concurrent users (which is quite a high number for a GUI web application). For us this would mean using global.asax instead since we are not
using ASP.NET MVC. But still, I am interested to know if there is a possiblity to only let the connection be opened when it is actually used instead of throughout the request. A simple "no" is fine enough for me. |
|
|
I'll give you a simple "no" then *grin*. At present the connection is always opened when the unit of work is created and closed when it is disposed, and there is no way to override this behaviour. So if fine control of the connection lifecycle is required, it effectively becomes a matter of managing the UOW lifecycle in the same way that you would have managed the connection: for example creating a UOW in Application_BeginRequest and disposing it in Application_EndRequest, or if you require even more rapid disposal you could possibly tie it to a controller lifecycle. It should be possible to encapsulate these patterns in a custom scope class (derived from UnitOfWorkScopeBase) if required. |
|
|
Ok, fine. I forgive you for this since you provide such an excellent O/R mapper in all other retrospects. This is fine for us now and we will create and dispose the UOW as you suggest. However, for a web site with heavy load, there is quite a performance boost to be gained if the database connection could be "opened" only during the timefram of when the database is actually accessed (as I am sure you know already) and then returned to thread pool. So perhaps this feature will come in a future version of Lightspeed? *grin* But I guess it is quite difficult (impossible?) thing to do with respect to the transaction management you have to support. |
|
|
Ok, I am about to go nuts on this thing........... Perhaps it is just a friday afternoon thing for me, but I'll trouble you with the disposing of the UOW that has kept me busy almost the the whole day. Hopefully my desciption of the problem rings a bell and you'll pop up with an easy solution. void Application_EndRequest(object sender, EventArgs e) with LightSpeedContextHolder.Current being my own wrapper class over LightSpeedContext<T>. This code works like a charm in several places in our code and the Dispose method is called successfully therefore avoiding a call to the Finalizer in the UnitOfWork class. Wonderfull! But. Then all of a sudden, during a normal UnitOfWork FindOne<TEntity> request on a domain model property, _disposed field is set to true although the UnitOfWork has not been Disposed yet. Naturally this throws an exceptions since you check that the object has not been disposed yet. I am pretty sure of that the object has not been disposed since I added a static objectReferenceCounted that gets incremented in the contsructor of the Lightspeed UnitOfWork code (we bought the source code). And the private _disposed is only set in the Dispose method... I figure most people that are using Lightspeed actually skip the closing of the connection with the Dispose method and let the Garbage Collector handle it via the Finaliser method. A bit unnecessary strain on the GC I think so I am hoping this can be solved. Easily. With a *grin* |
|
|
Hmm. The only thing I can think of is that there is some sort of race condition going on here. As you have obviously seen from the source code, the only way for a unit of work to become disposed is for someone to, well, call Dispose on it. And UnitOfWorkScopeBase calls Dispose on the contained UOW only when the UnitOfWorkScopeBase is being disposed. So could it be that somewhere along the path to that troublesome FindOne<>, something else is calling Dispose? Could you be spinning off multiple threads? Could you attach an event handler to IUnitOfWork.Disposed (or override OnDisposed in TangoUnitOfWork), put a breakpoint in the event handler and have a look at the call stack when that breakpoint gets hit? Thanks! |
|
|
Yes, I've had that thought too. But since I've already had a breakpoint set in the Dispose method I figured Visual Studio would stop execution for a second unknown running thread too. But sure,I'll try your additional approach as well. Unfortunatly it's gonna have to wait until tomorrow. Meetings all day long unfortunatly. I'll try the error scenario again tomorrow too. Perhaps Friday was just weird Friday... Let's hope. |
|
|
Hi A proposed solution: I'll have to think a bit of the implifications of this in our chosen architecture (basically according to Microsoft Web Client Software Factory guidelines), but the obvious solution is to avoid storing Lightspeed objects in Session (or in HttpContext.Cache) since you never can garantuee that it does not need to do a database access (eager load perhaps, but this would be convention thar easily could be missed.).Having the connection open in the session I think is a non viable option. So instead, sessions would only contain the id to the object and the object in its turn would have to be retrieved again from the Lightspeed Respository (data via Lightspeed cache or database). Would this be the approach you recommed or do you have any other suggestion? (I would like to avoid data transfer objects (POCO) in this case) . Like for instance hooking the connection to DB in again if it has been disposed... |
|
|
Yes, we would recommend the "always retrieve from repository when required" approach. In this way you can be sure that the object is within a UnitOfWork while you are using it. Another possible approach, that retains your current "store entities in session" design, is to put a strong-typed wrapper API around Session (if you haven't already done so), and in the properties/methods that get LightSpeed objects, attach those objects to the current unit of work. Obviously the danger here is that developers not aware of the rules can still access Session directly and wreak nine kinds of havoc. The benefit is slightly faster performance on a single machine but assuming LightSpeed caching and a well-set-up database that shouldn't be a big deal; and of course as you scale to more than one machine because ASP.NET has to serialise and deserialise the LightSpeed objects (to the state service or database) between requests, at which point you might as well retrieve them from the DB anyway. I don't think the problem would disappear if the database connection were hooked up only when required, because you would still have the problem that the entities belonged to a disposed UOW. It *would* mean you didn't need to dispose the UOW between requests, so you could have a PerSessionUnitOfWorkScope, which *would* solve the problem, but at the expense of creating a long-lived unit of work with all the problems that entails. E.g. what happens if request 1 of the session comes in on Box A, but request 2 comes in on Box B? What if ASP.NET recycles the worker process? In fact, you have to make the UnitOfWork itself serialisable... it all starts to get a bit scary. (As you rightly note, the transactions stuff also has the potential to be nasty because it means we can't just open the connection at the beginning of a query or lazy-load or SaveChanges and close it at the end: if someone starts a transaction, then does two SaveChanges, then commits the transaction, we have to use the same connection for those two SaveChanges and keep it open in the meantime. Which means we end up having to track the transaction state of the UOW and decide whether to open-close or reuse-and-hold. But even ignoring this I'm not convinced that a long-lived UOW with on-demand connection would be terribly feasible.) |
|
|
ok, I understand how you reason. Thanks for the speedy response. |
|
|
I'm sorry, but I have a follow up question that I think may be important. I've temporariliy chosen to attach the current UnitOfWork in a Session Controller (Instead of retrieveing it again from DB. I actually think this is a better solution, but for now we are not using the Lightspeed cache.). It works fine. However I hvae come to think of something. As it is now I use the following code:
If not, and I guess from a performance perspective it would be nice if UnitOfWork implements IEquatable<UnitOfWork> so that it is possible to compare one UnitOfWork against another one? Or am I missing a possible different way to do this? br, Tobias |
|
|
Wow!
|
|
|
I am probably being dumb here, but how would an IEquatable implementation differ from plain old reference equality? A UnitOfWork is very definitely a reference type; we wouldn't want to report two units of work as being equal just because they happened to have the same properties. Why not just check if (unitOfWorkScope.HasCurrent && unitOfWorkScope.Current != _unitOfWork)? Sorry if I've missed your point... |
|
|
Nope, you are not dumb. But I am a bit.. private IUnitOfWork _unitOfWork; What I was seeing as a problem was actually the AttachUnitOfWork method in my code. You suggested the change in my UnitOfWork property, and you are right, this change could be done in this way. However, in the AttachUnitOfWork I don't see you doing any check fist to see if the entity has not already been attached. I would have loved to see some documentation on what an attachmentof an already existing entity may incur. So basically, in order to be safe, I would like to do a check first on entity.UnitOfWork != UnitOfWork as you see. This is not possible since this getter is not exposed. Any proposals on this matter? btw, Looking in the other direction, a reference check before setting UnitOfWork property on Entity is not necessary since the Lightspeed Entity UnitOfWork property setter is doing a reference comparison first internally. But perhaps the opposite is not neccessary? Although from a performance perspective I guess it can make a difference.
|
|
|
Well, as you correctly point out, the (hypothetical) entity.UnitOfWork != UnitOfWork clause is unnecessary -- if this check fails, then the entity is already associated with UnitOfWork, and re-attaching it has no effect. So removing this clause from your if statement, i.e. performing the Attach even if the entity is already attached, would result in the same outcome. (And the performance impact is negligible -- LightSpeed will realise very quickly that nothing needs to be done and bail out early.) But it sounds from what you say though that your concern might be that the entity is already attached to *another* unit of work. Is that correct? If so we have a whole other discussion.. |
|
|
[quote user="ivan"] Well, as you correctly point out, the (hypothetical) entity.UnitOfWork != UnitOfWork clause is unnecessary -- if this check fails, then the entity is already associated with UnitOfWork, and re-attaching it has no effect. [/quote] Ok, great. May I perhaps suggest that you add that information to the documenatation? When I first read about the attach and add methods (in the old docs when Repository was used instead of UnitOfWork) I found it difficult to know the difference. Now I do, but some more documentation on the matter probably would have saved me some time. [quote user="ivan"] And the performance impact is negligible -- LightSpeed will realise very quickly that nothing needs to be done and bail out early.) [/quote] Yeap. I buy that too. [quote user="ivan"] But it sounds from what you say though that your concern might be that the entity is already attached to *another* unit of work. Is that correct? If so we have a whole other discussion.. [/quote] No, not yet. |
|