views:

169

answers:

2

In the following two cases, if Customer is disposable (implementing IDisposable), I believe it will not be disposed by ASP.NET, potentially being the cause of a memory leak:

    [WebMethod]
    public Customer FetchCustomer(int id)
    {
        return new Customer(id);
    }

    [WebMethod]
    public void SaveCustomer(Customer value)
    {
      // save it
    }

This (alleged) flaw applies to any IDisposable object. So returning a DataSet from a ASP.NET web service, for example, will also result in a memory leak - the DataSet will not be disposed [EDIT: This post claims that Dispose on a DataSet does nothing, so maybe this isn't such a problem]

In my case, Customer opened a database connection which was cleaned up in Dispose - except Dispose was never called resulting in loads of unclosed database connections. I realise there a whole bunch of bad practices being followed here, but the point is that ASP.NET - the (de)serializer - is responsible for disposing these objects, so why doesn't it?

This is an issue I was aware of for a while, but never got to the bottom of. I'm hoping somebody can confirm what I have found, and perhaps explain if there is a way of dealing with it.

+3  A: 

This is really a problem with your design, not with ASP.NET. The XmlSerializer it uses to serialize objects over SOAP doesn't know anything about the objects being serialized or whether or not they implement IDisposable. Moreover, it's not immediately apparent that they should be disposed, even if they do implement IDisposable; you might be returning a singleton instance, or an object in the cache.

Web services should accept and return message classes, AKA proxy classes, aka Data Transfer Objects, which are very simple, lightweight POCO classes without any real state or intelligence and especially no ownership of unmanaged resources.

You can use a tool like AutoMapper to quickly and easily map between your domain model classes like Customer (which apparently holds onto a database connection) and the DTOs that your web service uses.

Aaronaught
I do understand what you are saying, but as a general rule it is the responsibility of the code that creates an object instance to dispose of it. In my SaveCustomer example, the serializer creates the Customer instance, so shouldn't *it* be responsible for its disposal?
JulianM
@Serilla: Who says it is the responsibility of the code that creates an object instance to dispose of it? That's only true if the object will only be used *inside* that code. The XML Serializer isn't a lifetime manager, it's constructing an object specifically to give to you; it's *your* responsibility to make sure it gets disposed. Technically, when you `return` a disposable from a web method, it would be the *client's* responsibility to dispose it, but of course that can't happen, so you've painted yourself into a corner. That's why web methods normally work with DTOs.
Aaronaught
Because the client is unable to dispose the returned object, I wondered if it would be reasonable to expect ASP.NET to do so. Whether reasonable or not, the fact is that it doesn't. The golden rule, therefore, is not to return, or pass, objects implementing IDisposable (i.e. that may have unmanaged resources that require disposing). I don't think I've ever seen this stated explicitly anywhere before, so hopefully this will help somebody.
JulianM
+1  A: 

There might be exceptions to this rule, but in most cases, if a function returns an IDisposable object to you, it's now your problem to Dispose it.

That's why you're seeing the "leak". Yes, in time the GC will clean it up when memory is needed, but until it does, potentially important resources remain locked/in-use.

So remember the rule: It's it's IDisposable, Dispose of it when done!

=)

Will
With FetchCustomer there is no opportunity to dispose the object returned from the web method. The serializer will serialize the object into XML and return it to a client when called. Normally the client *would* dispose the object (as you've said), but in this case the client is not getting the same object, its getting a serialized version of it - in a different AppDomain, probably a different machine. Therefore, I'm saying the ASP.NET serializer should be responsible for disposing.
JulianM
Ah, I see you've edited your OP - it all makes sense now. Perhaps instead of returning the original Customer instance, perhaps create another (non-IDisposable) class based on Customer that is designed just for web service communication? (copying values to/from Customer and the other class, allowing you dispose when done) A side benefit is that it allows you to change the underlying Customer class without breaking the web contract.
Will
Yes, I think that is what Aaronaught is saying. While I agree that this is how things should/could be done, I was really questioning if it was reasonable that ASP.NET doesn't dispose of disposable objects that are instantiated or returned from a web service.
JulianM
Yes, agreed, it would be desirable and I guess not unreasonable in this instance. Perhaps chuck the instance in Context.Items and then write an IHttpModule that iterates thru everything in there at request end and calls Dispose on any IDisposable objects? I do something similar for DB connections and the like that I need to keep open for the life of the request, but must close at end.
Will