2

I'm having some problems with DataContexts using linq to sql for an asp.net c# web application.

I first had problems with exceptions being thrown as I didn't dispose of the DataContext, same errors as in this question. I was using a static DataContext which was probably why it wasn't disposed of properly, and after reading this and several other articles I wrapped all calls in using statements to be sure they'll get disposed of.

However, now I got problems when I need to update a foreign key as the DataContext has already been disposed of. I'm not sure why it's been disposed of already, and what the best practice would be to do in this scenario so any ideas would be greatly appreciated!

Short example here:

UPDATE: I think my example was too confusing when I tried to make it as short as possible so here's a longer and hopefully better example:

private static void SendTexts(List<TextAlert> TextQueue)
{
    using (THTDataContext db = new THTDataContext())
    {
        foreach (TextAlert text in TextQueue)
        {
            try
            {
                // do IntelliSMS stuff
                
                // set status to 'sent'
                text.Status = 1;

                db.SubmitChanges();
            }
            catch (IntelliSMSException ex)
            {
                // set status to 'failed'
                text.Status = 2;
                
                db.SubmitChanges();
            }
        }
    }
}

Thanks,

Annelie

Community
  • 1
  • 1
annelie
  • 2,569
  • 6
  • 25
  • 39
  • 1
    I always have wondered what the number two really does mean, thanks! ;) Seriously though, magic numbers have seen their day, an `enum` would be more effective (in terms of readability, if nothing else). And if `Status` is of an `enum` type/values then I'd suggest you use the proper type rather than assigning the values like so. Off-topic, I know, but I consider it decent information. – Grant Thomas Jan 26 '11 at 19:58
  • @Mr. Disappointment - Haha, I know, should have done it to start with instead of taking the quick option, it's been on my list of things to do for ages now. :) – annelie Jan 26 '11 at 20:16
  • It is very difficult to guess from your code snippet what you are actually doing: is "MyThing" a DB record created by Linq2SQL and is Status the foreign key field? – Christoph Jan 26 '11 at 20:34
  • @Christoph - Sorry, I thought it would be easier if I kept it short but I think I managed the opposite! :) Have updated my question now. MyThing was a list of text messages to be sent that I got from the database, and yes, status is the foreign key field. – annelie Jan 26 '11 at 20:47
  • And you got the elements in 'TextQueue' from another DataContext? Then I would give ladenedge's idea a try - including the "detatch" stunt. – Christoph Jan 26 '11 at 20:50
  • @Christoph - Yep, trying it now! – annelie Jan 26 '11 at 20:53
  • 1
    The best solution (also wrt. to performance) would probably be if you could keep the original DataContext alive until you have completed all your operations. If necessary, Dispose() explicitly without the using scope when you know that you are done. – Christoph Jan 26 '11 at 21:12
  • Yep, doing that now, thanks for the help! – annelie Jan 26 '11 at 21:20

2 Answers2

3

Yikes! If an exception was thrown while trying to update an entry in the database, do you really think it's a good idea in the exception handler to try and update an entry in the database?

As for what's going on here, what's the source of thing? Did it come from a query that was executed on another instance of MyDataContext? If so, that's your problem right there.

jason
  • 236,483
  • 35
  • 423
  • 525
  • I made the example as short as possible, it's actually an IntelliSMSException I'm catching (I'm trying to send text messages), the database exceptions are handled elsewhere. Is it still a bad idea to do it like this? And yep, the source of the thing was indeed executed on another instance. – annelie Jan 26 '11 at 20:14
  • 1
    +1 for pointing out the submit-catch-submit. @annelie, if I were you and I were targeting a specific exception type, I'd write the `catch` for that exact type (`IntelliSMSException`) and/or wrap that `try` block only around the code that's throwing it. Having done that, you should handle any DB errors separately. – Justin Morgan - On strike Jan 26 '11 at 20:22
  • @annelie: Then that's the issue. You need to detach `myThing` from the previous instance, and attach it to the new instance. – jason Jan 26 '11 at 20:26
  • 1
    @Justin Morgan - I've updated my question now, I was catching that exact type, only wrote Exception as I did as short an example as possible for writing on here (and as that wasn't part of the question I figured it didn't matter too much). – annelie Jan 26 '11 at 20:28
3

You probably need to attach the incoming MyThing to your new context. Something like this might work:

private static void DoMyStuff(MyThing thing)
{
    using (MyDataContext db = new MyDataContext())
    {
       db.MyThings.Attach(thing);
       thing.Status = 1;
       db.SubmitChanges();
    }
}

Once it's attached, the context should be able to track changes to it and, on the Submit, store them.

If this doesn't resolve the "already disposed" exceptions, then you might try "detaching" the MyThing before disposing of the old context. Note that if you have to go this route, however, the lifecycle of your DataContext is probably wrong, as entities are not designed to move about between contexts in the same app domain.

If you're interested in understanding the attach/detach business a bit better, MSDN's Dinesh Kulkarni has a short blog post about it.

ladenedge
  • 13,197
  • 11
  • 60
  • 117
  • Getting an error saying "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.", but I will read up on the link you sent and come back when I understand it a bit better. Thanks! – annelie Jan 26 '11 at 20:52
  • 2
    @annelie: likely a lifecycle problem, then. The links should help a bit, but the upshot is you probably need to widen the scope of your initial context such that you can `DoMyStuff` inside of that scope. A typical pattern would involve storing your DataContext in the HTTP context (if a web app) or thread-local storage (if not a web app). Good luck! – ladenedge Jan 26 '11 at 21:10
  • Just did that and now it works a treat! Wrapped the using around the calls to get the text queue and to send them and passed a reference, don't know why I didn't do that to start with but I'm going to blame having worked three 13 hours days and my brain just can't fix any more bugs before this deadline. ;) Thank you so much! – annelie Jan 26 '11 at 21:19