Search code examples
c#entity-frameworklinq-to-entities

c# entity framework with virtual property query


I have this class:

    public class Message
    {
        public Message()
        {
            Contacts = new List<Contact>();
        }

        public Message(string Sub_Message, string Body_Message, string Date_Send_Message)
        {
            Contacts = new List<Contact>();

            Subject = Sub_Message;
            Body = Body_Message;
            Date = Date_Send_Message;
        }

        public int MessageId { get; set; }
        public string Subject { get; set; }

        public string Date { get; set; }

        public string Body { get; set; }

        public virtual IList<Contact> Contacts { get; set; }
    }

I want to get the table of the Contact, because of that Messages is virtual and all the lazy loading stuff,

This call didn't work to me and got this error:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. in Reference Table

syntax:

    public ObservableCollection<Model.Message> LoadMessages()
    {
      using (db) {
       var x = from qr in db.Messages
                            order by qr.Subject
                            select qr;
          }
     }

While this works:

public ObservableCollection<Model.Message> LoadMessages()
{
    using (db)
    {
        var Messages = db.Messages.Include(z => z.Contacts).ToList();

        return new ObservableCollection<Model.Message>(Messages); 
    }
}

So I am using the queries inside a service called MessageService, Everytime I want to use the dbContext I create a function for it and put it inside using(db)

Like this:

     public class MessageService
        {
            ReadingFromData db = new ReadingFromData();
            public ObservableCollection<Model.Message> LoadMessages()
            {
                using (db)
                {
                    //Do something with db
                }
            }
}

Also, can someone explain to me how this works, and how to work with entity framework queries correct

Thanks


Solution

  • First of all, you should understand that this code does not query database:

    var x = from qr in db.Messages
            orderby qr.Subject
            select qr;
    

    it's just a query definition (expression) which should be translated into SQL and sent to the database server when you'll execute it. Executing is enumerating query results, or using one of LINQ operators with immediate execution type (see Classification of Standard Query Operators by Manner of Execution). I.e. when later in the code you'll enumerate x or if you'll try to store query results in a list, it is possible that database context db will be already disposed. And of course, you will get an error

    var x = db.Messages; // query is not executed
    db.Dispose(); // context disposed
    foreach(var m in x) // exception here, you try to execute query which uses disposed context
       ...
    

    Now about lazy-loading. It works by storing database context in the proxy entities which inherit from your entities. So actually db.Messages will return entities of some type MessageWithDbContext with db value stored internally. It is required for making additional 'lazy' database queries later. And again, if at that point of time database context is disposed, then you'll get an exception:

     var x = db.Messages.ToList(); // query is executed, messages are loaded
     db.Dispose();  // context disposed
     foreach(var m in x)
        m.Contacts.Count(); // exception - you try to execute contacts query with disposed db
    

    How to solve this issue? Either make sure that database context is not disposed while you are working with the query and making additional 'lazy' calls. Or use eager loading as you are doing in the second example. Eager loading allows you to load related entities when you are executing the query:

     // both messages and contacts are loaded from database when you execute the query
     var x = db.Message.Include(m => m.Contacts).ToList();
     db.Dispose();
     foreach(var m in x)
        m.Contacts.Count();
    

    In this case no additional 'lazy' calls are required, so you can dispose database context and work with query results.