views:

142

answers:

3

what is the preferred practice when linq2sql using (in asp.net mvc applications): to create "singleton" for DataContext like:

partial class db
{
    static db _db = new db(global::data.Properties.Settings.Default.nanocrmConnectionString, new AttributeMappingSource());

    public static db GetInstance()
    {
        return _db;
    }
}

or to retrieve new instance when it needed within using:

using (db _db = new db())
{
    ...
}

the usage of using brings some limitations into code. so I prefer to use singleton one. is it weird practice?

UPD:
explanation why i'm using singleton:

public class UserGroupRepository
{
    public static IQueryable<Group> RolesFor(string username)
    {
        User user = UserRepository.WithUsername(username);

        return from g in db.GetInstance().Groups
                join ug in db.GetInstance().UsersGroups on g.Id equals ug.GroupId
                where ug.UserId == user.Id
                select g;
    }
}

i have this method. due to it returns IQueryable - I can continue composing query without it executing, so here just lazy result returns.
if i rewrite the same code with using - i cannot be able to return IQueryable (because db will be disposed and IQueryable will be lost too), and i would change it to List. and now this method will return "huge" List from which i will filter data on previous function.

i hope i describe enough detailed.

+3  A: 

Linq to Sql data contexts are NOT thread safe, and should only be used within the context of a single thread. Using the singleton pattern is not only contrary to standard linq2sql practices, but will result in serious problems if your application comes under any sort of serious load.

EDIT:

In response to your limitations with regard to the using block, try implement your RolesFor method as an extension method:

public static IQueryable<Group> GetUserRoles(this Database db, string username)
{
        return from g in db.GetInstance().Groups
                join ug in db.GetInstance().UsersGroups on g.Id equals ug.GroupId
                where ug.UserId == user.Id
                select g;
}

This would allow you to call your method inside a using block from anywhere:

using(Database db = createContext())
{
     IQueryable<Group> queryable = db.GetUserRoles("MyUsername");
     // from here on you can query the queryable object
     var groups = (from g in queryable
                   where g.Name == "MyRole"
                   select g).ToList();
}

EDIT 2

In reponse to your comment about opening another connection to the sql server for each instance of the data context. Creating a datacontext will not open a connection to the sql server, but each actual operation will. Regardless of whether you create 1 or 4 datacontexts, if you are performing 4 operations on the database, 4 sqlconnections will be opened. However, keep in mind that .NET uses a sql server connection pool, so each operation doesn't require the creation of an entirely new SqlConnection, but only the retrieval of an existing one from the connection pool and the reopening of the connection

LorenVS
@LorenVS: i add **UPD** to original question with explain why I asked the question.
zerkms
this will work, but this wastes `db` class and is conceptually wrong.
zerkms
@zerkms, please clarify what you mean, in what ways is it conceptually wrong and what do you mean by wasting db class
LorenVS
@LorenVS: any logic of working with `groups` should be incapsulated inside of `UserGroupRepository` or corresponding service layer class, not within `db` class
zerkms
@zerkms, you could just turn the extension method into a normal method, and have it take the Db object as an argument, and place that method into your usergrouprepository...
LorenVS
@LorenVS: i think that the best way will be to rethink of how to do "chain filtering". current implementation is too limited, i bet i can find architect solution which will be compatible to .net specifics (i mean GC dispose)
zerkms
@LorenVS: i know how it works, but it still extends db class, despite it locates within repository one ;-)
zerkms
@zerkms, if you're that worried about how the functionality appears, then your only real proper option is to create a class that implements IDisposable and IQueryable<T>, and containing an instance of your Db class... Such a class would create the appropriate IQueryable internally, and would expose the methods of IQueryable<T> . You could then use that class in your using statement (your class's dispose method would call the dispose method of the db object, which is perfectly safe)
LorenVS
@LorenVS: yep, i get it. but i think it's too complex for me now. so i will try to reimplement in simple and able-to-implement solution for first :-)
zerkms
@LorenVS: http://en.csharp-online.net/Singleton_design_pattern:_Thread-safe_Singleton - here i get that my implementation is thread safe
zerkms
@zerkms, it is not your singleton pattern that is not threadsafe. Your singleton pattern will work just fine. The problem here is that the actual DataContext object IS NOT thread safe. Your singleton pattern ensures that only 1 DataContext is created, regardless of threading conditions (which it does properly), but there is a problem regardless when you have multiple threads accessing the same Linq2Sql DataContext.
LorenVS
ah...... %) yep http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx?ppud=4 - "Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe." well, thanks for this response too :-) upvoted all your answers here as appreciation for such detailed explanations.
zerkms
Thanks for the upvotes, just trying to help, I suppose I could have posted links earlier...
LorenVS
A: 

You'll probably want to manage the lifetime of your context to be scoped to a single web request and have only one context for the lifetime of that web request cycle.

Google search for 'web scoped objectcontext' or 'objectcontext lifetime' (or datacontext for l2s).

e.g. http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx

In MVC2 you can put the context management code in your base controller class.

Hightechrider
yep, this will work too, but looks like `diiiiiiirty hack`
zerkms
No, this is common practice. See also http://stackoverflow.com/questions/196253/linq-to-sql-where-does-your-datacontext-live
Hightechrider
so, if i create one datacontext stored per request (with storing them in dictionary as described in "One shared ObjectContext instance per HTTP request") - how they will be disposed later? after request was served all of HttpContext.Current.Items (or even HttpContext.Current) will be garbage collected automatically?
zerkms
You can just instantiate one in your base controller class or controller factory (easier than using HttpContext if you don't need it). They will be garbage collected sometime after request is done, yes.
Hightechrider
+1  A: 

Linq to SQL wants you to create a context per operation. In fact, the Data Load Options can only be set for executing the first query, so if you want to do loading hints, you have to do it that way. However, when you have a 3-tier architecture, you will run into the problem that objects from one datacontext can't really work with objects from a different context.

Working around that is a real pain, so we just did a context per request for web stuff and a thread local approach for Windows Services and the like.

Jarret R
yep, as Hightechrider mentioned above, http-request-context will solve the "trouble", but for me (and i bet for you) it looks like dirty hack, isn't it?
zerkms
I still find the contest per request approach slightly nasty, especially given the tendency of web developers to retrieve data in an asynchronous matter... That may not be the norm for your sites, but if somebody decides to add an asynchronous retrieval in the future, you have a multi-threaded bug waiting to happen... I would say your best bet in this situation would be to translate the Linq2Sql objects into your own business objects...
LorenVS