2

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.

I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.

I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:

"All new entities within an object graph must be added/attached before changes are submitted."

this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.

With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.

I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,

I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.

Here is my code:

using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
        {

            SPSite sitecollection = new SPSite("http://dev");
            SPWeb web = sitecollection.OpenWeb();
            SPList theOldList = web.Lists.TryGetList("OldList_Physicians");

             //Create new Physician entities.
                    foreach(SPListItem l in theOldList.Items)
                    {
                        PhysiciansItem p = new PhysiciansItem()
                        {
                            FirstName = (String)l["First Name"],
                            Title = (String)l["Last Name"],  
                            MiddleInitial = (String)l["Middle Init"],
                            ProviderNumber = Convert.ToInt32(l["Provider No"]),
                            Gender = ConvertGender(l),
                            UndergraduateSchool =(String)l["UG_School"],
                            MedicalSchool = (String)l["Med_School"], 
                            Residency = (String)l["Residency"],
                            Fellowship = (String)l["Fellowship"], 
                            Internship = (String)l["Internship"],    
                            PhysicianType = ConvertToPhysiciantype(l),
                            Specialty = ConvertSpecialties(l),
                            InsurancesAccepted = ConvertInsurance(l),
                        };
                   ctx.Physicians.InsertOnSubmit(p);
                    }

                    ctx.SubmitChanges(); //this is where it flakes out
        }
    }


     //Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.

    private Gender ConvertGender(SPListItem l)
    {
        Gender g = new Gender();
        if ((String)l["Sex"] == "M")
        {
            g = Gender.M;
        }
        else g = Gender.F;
        return g;

    }

    //Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and 
    //DO (Doctor of Osteopathic Medicine).  State Regualtions require this information to be attached 
    //to a physician's profile.
    private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
    {
        ProviderTypesItem p = new ProviderTypesItem();
        p.Title = (String)l["Provider_Title:Title"];
        p.Intials = (String)l["Provider_Title"];

        return p;
    }

    //Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
    private EntitySet<Item> ConvertSpecialties(SPListItem l)
    {
        EntitySet<Item> theEntityList = new EntitySet<Item>();
        Item i = new Item();
        i.Title = (String)l["Provider Specialty"];
        theEntityList.Add(i);            

        if ((String)l["Provider SubSpecialty"] != null)
        {
            Item theSubSpecialty = new Item();
            theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
            theEntityList.Add(theSubSpecialty);
        }

        return theEntityList;
    }


    //Process and add insurance accepted.
    //Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
    //list.
    private EntitySet<Item> ConvertInsurance(SPListItem l)
    {
        EntitySet<Item> theEntityList = new EntitySet<Item>();
           if ((bool)l["TennCare"] == true)
                {
                    Item TenncareItem = new Item();
                    TenncareItem.Title = "TennCare";
                    theEntityList.Add(TenncareItem);

                }
         if ((bool)l["Medicare"] == true)
                {
                    Item MedicareItem = new Item();
                    MedicareItem.Title = "Medicare";
                    theEntityList.Add(MedicareItem);
                }
         if ((bool)l["Commercial"] == true)
                {
                    Item CommercialItem = new Item();
                    CommercialItem.Title = "Commercial";
                    theEntityList.Add(CommercialItem);
                }
         return theEntityList;

   }
}
gev125
  • 152
  • 3
  • 9

1 Answers1

2

So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).

You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)

  1. Generate the entity for just that one list (Physicians) in some temporary folder
  2. Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
  3. Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
  4. Paste the lookup id & value properties in this partial Physicians class.

Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:

  • PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
  • PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
  • PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.

It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

Servy
  • 202,030
  • 26
  • 332
  • 449