SetAsDeleteOnSubmit() throwing exception

May 20, 2011 at 9:53 PM

Greetings,

I have an object structure that looks like this:  Incsetupmain -> Inccluster(s).  I'm trying to delete a cluster withing the Incclusters collection on a Incsetupmain object.  When I attempt to use the SetAsDeleteOnSubmit() method, I receive an exception on this line of code in the LINQEntityBase.cs file:

            if (this.LINQEntityState == EntityState.NotTracked)
                throw new ApplicationException("You cannot change the Entity State when the Entity is not change tracked");
Here is the code to retrieve the data:
           Incsetupmain bank;

            using (PbtestmainDataContext db = new PbtestmainDataContext())
            {
                var dataLoadOptions = new Devart.Data.Linq.DataLoadOptions();
                dataLoadOptions.LoadWith<Incsetupmain>(m => m.Incclusters);
                db.LoadOptions = dataLoadOptions;

                bank = (from incsetupmain in db.Incsetupmains
                        where incsetupmain.Nincsetupkey == incSetupKey
                        select incsetupmain).First();

                        return bank;
              }
  
The client calls the method to retrieve the data via a WCF service so we're dealing with a disconnected set of data. The client code looks like the following:
            Incsetupmain bank = LoadBank(bankKey);

            // Tell the root object it's doing the change tracking                              
            bank.SetAsChangeTrackingRoot();


            List<Inccluster> clustersDelete = (from clusters in bank.Incclusters
                                               where clusters.Nmaxpoints == 1000 && clusters.Nminpoints == 1000 && clusters.LINQEntityState != EntityState.New
                                               select clusters).ToList();
            // Delete some orders (NOTE: it's using the flat list!)
            //List<Inccluster> clustersDelete = (from clusters in bank.ToEntityTree().OfType<Inccluster>()
            //                                   where clusters.Nmaxpoints == 1000 && clusters.Nminpoints == 1000 //&& clusters.LINQEntityState != EntityState.New
            //                                   select clusters).ToList();

            foreach (Inccluster clusterDeleted in clustersDelete)
            {
                //bank.Incclusters.Remove(clusterDeleted);
                clusterDeleted.SetAsDeleteOnSubmit();
                Console.WriteLine("Deleted  {0} --> {1}", clusterDeleted.LINQEntityGUID, clusterDeleted.GetType().Name);
            }
In the for loop when I use SetAsDeleteOnSubmit, I receive the exception as stated above. It's also interesting to note if I try to use the bank.ToEntityTree() I don't receive any of the Incclusters underneath the bank. I'm not sure what I'm doing wrong. Any suggestions are appreciated.
May 20, 2011 at 10:59 PM

Hi there,

Seeing that  when you do bank.ToEntityTree().OfType<Inncluster>() it's not picking up any records, it doesn't recognise the entities as children.

Just a few things to try:

1) DeferredLoadingEnabled needs to be set to false when you retrieve the data, this must be on for everything to work correctly in a disconnected mode:

db.DeferredLoadingEnabled = false;

2) Make sure you are using the DataContractSerializer in WCF and that you have serialization turned on in your dbml file (uni-directional).

Apart from that I could not see any reason why it would not work...

Just also double check with the code int the example shipped with the LINQ to SQL Entity Base Class... I believe there should be examples of deletions in there (over WCF).

Cheers

Matt.

May 23, 2011 at 4:10 PM

Thank you for responding so quickly.  I added the DeferredLoadingEnabled with no luck.  One thing I should mention, I'm using the DevArt LinqConnect product as an ORM.  The attributes that decorate the collection are slightly different than the sample provided on Codeplex (see below). I'll roll up my sleeves and determine why the _entityTree only recognizes the root node.

        [Devart.Data.Linq.Mapping.Association(Name="Incsetupmain_Inccluster", Storage="_Incclusters", OtherKey="Nincsetupkey")]
        [DataMember(Order=30, EmitDefaultValue=false)]
        public EntitySet<Inccluster> Incclusters
        {
            get
            {
                if ((this.serializing
                  && (this._Incclusters.HasLoadedOrAssignedValues == false) && (this._Incclusters.IsDeferred || this._Incclusters.Count == 0)))
                {
                    return null;
                }
                return this._Incclusters;
            }
            set
            {
                this._Incclusters.Assign(value);
            }
        }
May 23, 2011 at 6:47 PM

I think I have found a working solution.  I needed to change the return type of the GetCustomAttribute call found in GetImportantProperties.  Now ToEntityTree returns the entire flattened structure.  Hopefully I won't run into too much code that needs tweaking.  This code has been quite useful.  Thanks again.  Code snipped below:

                foreach (PropertyInfo propInfo in type.GetProperties())
                {
                    // check it's an association attribute first
                    devartAssociateAttribute = (Devart.Data.Linq.Mapping.AssociationAttribute)Devart.Data.Linq.Mapping.AssociationAttribute.GetCustomAttribute(propInfo, typeof(Devart.Data.Linq.Mapping.AssociationAttribute), false);

                     // if it is an association attribute
                    if (devartAssociateAttribute != null)
                    {
                        // Store the FK relationships seperately (i.e. child to parent relationships);
                        if (devartAssociateAttribute.IsForeignKey != true)
                            _cacheAssociationProperties[type].Add(propInfo.Name, propInfo);
                        else
                            _cacheAssociationFKProperties[type].Add(propInfo.Name, propInfo);
                    }
                    else // check if its a column attribute second, less common
                    {
                        colAttribute = Attribute.GetCustomAttribute(propInfo, typeof(ColumnAttribute), false) as ColumnAttribute;

                        if (colAttribute != null && colAttribute.IsDbGenerated == true)
                        {
                            _cacheDBGeneratedProperties[type].Add(propInfo.Name, propInfo);
                            continue;
                        }
                    }
                }