views:

290

answers:

1

I'm new to the entity framework and I'm really confused about how savechanges works. There's probably a lot of code in my example which could be improved, but here's the problem I'm having.

The user enters a bunch of picks. I make sure the user hasn't already entered those picks. Then I add the picks to the database.

       var db = new myModel()
       var predictionArray = ticker.Substring(1).Split(','); // Get rid of the initial comma.

        var user = Membership.GetUser();
        var userId = Convert.ToInt32(user.ProviderUserKey);

        // Get the member with all his predictions for today.
        var memberQuery = (from member in db.Members
                         where member.user_id == userId
                         select new
                                    {
                                        member,
                                        predictions = from p in member.Predictions
                                                     where p.start_date == null
                                                     select p

                                    }).First();

        // Load all the company ids.
        foreach (var prediction in memberQuery.predictions)
        {
            prediction.CompanyReference.Load();
        }

 var picks = from prediction in predictionArray
                          let data = prediction.Split(':')
                          let companyTicker = data[0]
                          where !(from i in memberQuery.predictions
                                 select i.Company.ticker).Contains(companyTicker)
                          select new Prediction
                          {
                              Member = memberQuery.member,
                              Company = db.Companies.Where(c => c.ticker == companyTicker).First(),
                              is_up = data[1] == "up",  // This turns up and down into true and false.
                          };


    // Save the records to the database.
    // HERE'S THE PART I DON'T UNDERSTAND.
    // This saves the records, even though I don't have db.AddToPredictions(pick) 
            foreach (var pick in picks)
            {
                    db.SaveChanges();
            }

// This does not save records when the db.SaveChanges outside of a loop of picks.
 db.SaveChanges();
 foreach (var pick in picks)
            {

            }

// This saves records,  but it will insert all the picks exactly once no matter how many picks you have.  
//The fact you're skipping a pick makes no difference in what gets inserted.
            var counter = 1;
            foreach (var pick in picks)
            {
                if (counter == 2)
                {
                    db.SaveChanges();
                }
                counter++;
            }

I've tested and the SaveChanges doesn't even have to be in the loop. The below code works, too.

foreach (var pick in picks)
            {
                   break;
            }

db.SaveChanges()

There's obviously something going on with the context I don't understand. I'm guessing I've somehow loaded my new picks as pending changes, but even if that's true I don't understand I have to loop over them to save changes.

Can someone explain this to me?

Here's updated working code based on Craig's responses: 1) Remove the Type then loop over the results and populate new objects.

var  picks = (from prediction in predictionArray
                        let data = prediction.Split(':')
                        let companyTicker = data[0]
                        where !(from i in memberQuery.predictions
                                select i.Company.ticker).Contains(companyTicker)
                        select new //NO TYPE HERE
                        {
                            Member = memberQuery.member,
                            Company = db.Companies.Where(c => c.ticker == companyTicker).First(),
                            is_up = data[1] == "up",  // This turns up and down into true and false.
                        }).ToList();

            foreach (var prediction in picks)
            {
              if (includePrediction)
               {
                var p = new Prediction{
                  Member = prediction.Member,
                  Company = prediction.Company,
                  is_up = prediction.is_up
                 };
                db.AddToPredictions(p);
                }
            }

2) Or if I don't want the predictions to be saved, I can detach the predictions.

foreach (var prediction in picks) {
                  if  (excludePrediction)
                  {
                      db.Detach(prediction)
                   }

}
+1  A: 

The reason is here:

                      select new Prediction
                      {
                          Member = memberQuery.member,

These lines will (once the IEnumerable is iterated; LINQ is lazy) :

  1. Instantiate a new Prediction
  2. Associate that Prediction with an existing Member, *which is attached to db.

Associating an instance of an entity with an attached entity automatically adds that entity to the context of the associated, attached entity.

So as soon as you start iterating over predictionArray, the code above executes and you have a new entity in your context.

Craig Stuntz
I think I'm starting to grasp this. How do I detach the predictions if I want to? I also found this (for anyone else reading this) which explains the deferred execution you mentioned which helped me understand why I needed to call my picks variable before I saved. I've replaced the loop with the ToList() command. http://www.codewrecks.com/blog/index.php/2009/01/08/entity-framework-first-steps/
tmfkmoney
You detach with Context.Detach. But you can also just project onto an anonymous type (or other POCO) instead of onto a new entity instance.
Craig Stuntz
Thanks, Craig. I've updated my question based on your responses. This was a big help.
tmfkmoney