Field Members vs Method Variables?

2019-04-02 07:19发布

问题:

Recently I've been thinking about performance difference between class field members and method variables. What exactly I mean is in the example below :

Lets say we have a DataContext object for Linq2SQL

class DataLayer
{
    ProductDataContext context = new ProductDataContext();

    public IQueryable<Product> GetData()
    {
       return context.Where(t=>t.ProductId == 2);
    }
}

In the example above, context will be stored in heap and the GetData method variables will be removed from Stack after Method is executed.

So lets examine the following example to make a distinction :

class DataLayer
{
    public IQueryable<Product> GetData()
    {
       ProductDataContext context = new ProductDataContext();
       return context.Where(t=>t.ProductId == 2);
    }
} 

(*1) So okay first thing we know is if we define ProductDataContext instance as a field, we can reach it everywhere in the class which means we don't have to create same object instance all the time.

But lets say we are talking about Asp.NET and once the users press submit button the post data is sent to the server and the events are executed and the posted data stored in a database via the method above so it is probable that the same user can send different data after one another.If I know correctly after the page is executed, the finalizers come into play and clear things from memory (from heap) and that means we lose our instance variables from memory as well and after another post, DataContext should be created once again for the new page cycle.

So it seems the only benefit of declaring it publicly to the whole class is the just number one text above.

Or is there something other?

Thanks in advance...

(If I told something incorrect please fix me.. )

回答1:

When it comes to the performance difference between creating an object per method or per class instance, I wouldn't worry to much about it. However, what you seem to miss here are some important principles around the DataContext class and the unit of work pattern in general.

The DataContext class operates as a single unit of work. Thus, you create a DataContext, you create objects, update and delete objects, you submit all changes and you dispose the DataContext after that. You may create multiple DataContext classes per request, one per (business) transaction. But in ASP.NET you should never create a DataContext that survives a web request. All the DataContexts that are created during a request should be disposed when or before that request is over. There are two reasons for this.

First of all, the DataContext has an internal cache of all objects that it has fetched from the database. Using a DataContext for a long period of time will make its cache grow indefinitely and can cause memory problems when you’ve got a big database. The DataContext will also favor returning an object from cache when it can, making your objects go stale quickly. Any update and delete operation made on another DataContext or directly to the database can get unnoticed because of this staleness.

Second reason for not caching DataContexts, is that they are not thread-safe. It’s best to see a DataContext as a unit of work, or as a (business) transaction. You create a bunch of new objects, add them to the DataContext, change some others, remove some objects and when you’re done, you call SubmitChanges. If another request calls SubmitChanges on that same instance during that operation, you are losing the idea of the transaction. When you are allowing code to do this, in the most fortunate situation, your new objects will be persisted and your transaction is split up in two separate transactions. At worst, you leave your DataContext, or the objects it persists in an invalid state, which could mean other requests fail or invalid data enters your database. And this is no unlikely scenario, I’ve seen strange things happen on projects were developers created a single (static) DataContext per web site.

So with this in mind, let’s get back to your question. While defining a DataContext as instance field is not a problem, it is important to know how you are using the DataLayer class. When you create one DataLayer per request or on per method call, you’ll probably be safe, but in that case you shouldn’t store that DataLayer in a static field. When you want to do that, you should create a DataContext per method call.

It is important to know what the design of the DataLayer class is. In your code you only show us a query method. No CUD methods. Is every method meant to be a single transaction, or do you want to call multiple methods and call a SaveChanges on the DataLayer afterwards? When you want this last option, you need to store the DataContext as an instance field and in that case you should implement IDisposable on the DataLayer. When every method is its own transaction, you can create a DataContext per method and you should wrap a DataContext in a using statement. Note however, that disposing the DataContext can cause problems when you return objects with lazy loading properties from a method. Those properties cannot be loaded anymore when the DataContext is disposed. Here is more interesting information about this.

As you see, I haven’t even talked about which of your two options would be better for performance, because performance is of no importance when the solution gives inconsistent and incorrect results.

I'm sorry for my long answer :-)



回答2:

You don't ever want to store a DataContext class on the class level. If you do, then you will have to implement the IDisposable interface on your class and call the Dispose method when you know you are done with it.

It's better to just create a new DataContext in your method and use a using statement to automatically dispose of it when you are done.

Even though the implementation of IDisposable on DataContext does nothing, that is an implementation detail, whereas exposing an IDisposable interface is a contract which you should always abide by.

It be especially handy if you upgrade to LINQ-to-Entities and use the ObjectContext class where you must call Dispose on the instance when you are done with it, otherwise, resources will leak until the next garbage collection.



回答3:

So it seems the only benefit of declaring it publicly to the whole class is the just number one text above.

Yes, declaring a class level variable is to allow the entire class to access the same variable. It should not be used to try and deliberately prevent a Garbage Collection from occurring. The access modifier on properties, methods etc. is used to determine what objects external or internal to your class can access/modify/monkey with that piece of code.

In ASP.NET once the request is sent to the browser, the created objects for that page request will get CGed at some point in time in the future, regardless of whether or not the variable is public. If you want information to stay between requests, you either need to create a singleton instance of the object, or serialize the object to either the session or application state.



回答4:

See this for example - "Linq to SQL DataContext Lifetime Management": http://www.west-wind.com/weblog/posts/246222.aspx This approach makes life simpler.



标签: c# asp.net class