Somwewhat automated delete functionality for databinding

Posts   
 
    
tolga
User
Posts: 25
Joined: 26-Feb-2006
# Posted on: 26-Feb-2006 23:03:28   

Hi,

I have read most of the posts regarding the logic behind not including automatic delete functionality into EntityCollection.

I understand the reasons, but yet would like to automate some of the functionality for myself.

So, I came up with some code derived from EntityCollection that keeps track of the deleted entities.

I am positive this technique would work, but I have a few questions: 1) Why is my EntityCollection derived class not showing any of the entity factories in the design-time drop-down? What can I do to make this have the same design-time functionality as EntityCollection? 2) Are there some major architectural reasons as to why not to use this technique? 3) As the 2.0 release nearing, I am relatively sure you know at this point whether you will include this type of functionality out of the box. Can you please let us know?

using System; using System.Collections; using System.ComponentModel; using System.Runtime.Serialization; using System.Xml;

using JetHavenLLBLData.EntityClasses; using JetHavenLLBLData.HelperClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;

namespace JetHavenData {

[Serializable]
public partial class JetHavenCollection : EntityCollection
{
    private EntityCollection _deletedEntities = new EntityCollection();

    public EntityCollection DeletedEntities
    {
        get { return _deletedEntities; }
    }

    public JetHavenCollection():base()
    {
        this.BeforeRemove += new EventHandler(JetHavenCollection_BeforeRemove);
    }

    void JetHavenCollection_BeforeRemove(object sender, EventArgs e)
    {
        _deletedEntities.Add(sender as IEntity2);
    }


    public EntityCollection DirtyEntities
    {
        get
        {
            EntityCollection ec = new EntityCollection(this.EntityFactoryToUse);
            foreach (EntityBase2 de in this.DirtyEntities)
                ec.Add(de);
            return ec;
        }
    }

    public void ClearDeletedEntities()
    {
        _deletedEntities.Clear();
    }

    public JetHavenCollection(IEntityFactory2 entityFactoryToUse):base(entityFactoryToUse)
    {
    }


    public JetHavenCollection(IEntityFactory2 entityFactoryToUse, IValidator validatorToUse):base(entityFactoryToUse, validatorToUse)
    {
    }


    protected JetHavenCollection(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }


}

}

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39930
Joined: 17-Aug-2003
# Posted on: 27-Feb-2006 18:14:22   

tolga wrote:

Hi,

I have read most of the posts regarding the logic behind not including automatic delete functionality into EntityCollection.

I understand the reasons, but yet would like to automate some of the functionality for myself.

So, I came up with some code derived from EntityCollection that keeps track of the deleted entities.

I am positive this technique would work, but I have a few questions: 1) Why is my EntityCollection derived class not showing any of the entity factories in the design-time drop-down? What can I do to make this have the same design-time functionality as EntityCollection?

The code in the toolboxitem is pretty limited, as vs.net 2002/3 doesn't offer rich type retrieval. This means that the toolboxitem scans the assembly in which the entitycollection class is in (or better: the type the toolboxitem is associated with, which is normally the generated entitycollection class) for IEntityFactory2 implementations.

So it's best to add your own entitycollection derived class to the assembly with the generated code.

2) Are there some major architectural reasons as to why not to use this technique?

automatic deletes? It has serious drawbacks, but I understand you learned them while reading the threads about this on the forum?

3) As the 2.0 release nearing, I am relatively sure you know at this point whether you will include this type of functionality out of the box. Can you please let us know?

This won't be added to v2 and likely also not beyond that version, as it's a fundamental problem which has a solution in the Unitofwork.

Frans Bouma | Lead developer LLBLGen Pro
tolga
User
Posts: 25
Joined: 26-Feb-2006
# Posted on: 03-Mar-2006 00:15:15   

But you could add a internal collection to EntityCollection to keep track of deleted entities.

There could be a method to clear this internal collection. So the user can start a new state by clearing the deletedentities collection and then at some point get the deletedentities collection to call the DataAccessAdapter.DeleteEntityCollection method.

This way, you're not giving the user the ability to automatically deleted the deletedentities from the database. You're just providing a convenience mechanism to keep track of deletedentities with which the user can do whatever they want...

I believe this is similar to whoever proposed the MarkAsDeleted functionality in another post, except it adds the functionality of keeping track of the deletedentities from a winform GUI perspective...

Thanks,

Tolga

This is the following code I have turned EntityCollection into because it really saves a lot of work:

/////////////////////////////////////////////////////////////// // This is generated code. If you modify this code, be aware // of the fact that when you re-generate the code, your changes // are lost. If you want to keep your changes, make this file read-only // when you have finished your changes, however it is recommended that // you inherit from this class to extend the functionality of this generated // class or you modify / extend the templates used to generate this code. ////////////////////////////////////////////////////////////// // Code is generated using LLBLGen Pro version: 1.0.2005.1 // Code is generated on: Sunday, February 26, 2006 2:20:15 PM // Code is generated using templates: C# template set for SqlServer (1.0.2005.1) with Web Serice support. // Templates vendor: Solutions Design. // Templates version: 1.0.2005.1.102305 ////////////////////////////////////////////////////////////// using System; using System.Collections; using System.ComponentModel; using System.Runtime.Serialization; using System.Xml;

using JetHavenLLBLData.EntityClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;

namespace JetHavenLLBLData.HelperClasses {

// __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
// __LLBLGENPRO_USER_CODE_REGION_END

/// <summary>
/// Generic Collection class for storing collections of entities of the same type.
/// </summary>
[Serializable]
public partial class EntityCollection : EntityCollectionBase2
{
    private ArrayList _deletedEntities = new ArrayList();

    public EntityCollection DeletedEntities
    {
        get 
        {
            EntityCollection foo = new EntityCollection();
            foreach (EntityBase2 e in _deletedEntities)
                foo.Add(e);
            return foo; 
        }
    }

    /// <summary>
    /// CTor
    /// </summary>
    public EntityCollection():base()
    {
        this.BeforeRemove += new EventHandler(EntityCollection_BeforeRemove);
    }


    /// <summary>
    /// CTor
    /// </summary>
    /// <param name="entityFactoryToUse">The entity factory object to use when this collection has to construct new objects.
    /// This is the case when the collection is bound to a grid-like control for example.</param>
    public EntityCollection(IEntityFactory2 entityFactoryToUse):base(entityFactoryToUse)
    {
        this.BeforeRemove += new EventHandler(EntityCollection_BeforeRemove);
    }


    /// <summary>
    /// CTor
    /// </summary>
    /// <param name="entityFactoryToUse">The entity factory object to use when this collection has to construct new objects.
    /// This is the case when the collection is bound to a grid-like control for example.</param>
    /// <param name="validatorToUse">The validator object to use for new entities constructed using the entity factory. Ignored when null</param>
    public EntityCollection(IEntityFactory2 entityFactoryToUse, IValidator validatorToUse):base(entityFactoryToUse, validatorToUse)
    {
        this.BeforeRemove += new EventHandler(EntityCollection_BeforeRemove);
    }


    /// <summary>
    /// Protected CTor for deserialization
    /// </summary>
    /// <param name="info"></param>
    /// <param name="context"></param>
    protected EntityCollection(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        this.BeforeRemove += new EventHandler(EntityCollection_BeforeRemove);
    }



    void EntityCollection_BeforeRemove(object sender, EventArgs e)
    {
        _deletedEntities.Add(sender as IEntity2);
    }


    public EntityCollection DirtyEntities2
    {
        get
        {
            EntityCollection ec = new EntityCollection(this.EntityFactoryToUse);
            foreach (EntityBase2 de in this.DirtyEntities)
                ec.Add(de);

            return ec;
        }
    }

    public void ClearDeletedEntities()
    {
        _deletedEntities.Clear();
    }

    /// <summary>
    /// ITypedList.GetItemProperties implementation. Necessary for Complex databinding. 
    /// </summary>
    /// <param name="listAccessors">Data to determine which property descriptor set to create</param>
    /// <returns>collection of property descriptors which will be used to create property related objects, like columns in a bound grid.</returns>
    public override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        // determine the type of the entity to return the properties of.
        if((listAccessors==null)||listAccessors.Length==0)
        {
            // this collection is bound to the control, simply call the base class routine to create property descriptors.
            return base.GetItemProperties(listAccessors);
        }

        // use the last entry in the listAccessors, grab its TypeContainedAttribute and instantiate an instance of the type in that attribute,
        // use that entity instance to produce properties.

if CF

        object[] customAttributes = listAccessors[listAccessors.Length-1].ComponentType.GetCustomAttributes(typeof(TypeContainedAttribute), false);
        TypeContainedAttribute typeAttribute = null;
        if(customAttributes.Length>0)
        {
            typeAttribute = (TypeContainedAttribute)customAttributes[0];
        }

else

        TypeContainedAttribute typeAttribute = (TypeContainedAttribute) listAccessors[listAccessors.Length-1].Attributes[typeof(TypeContainedAttribute)];

endif

        if(typeAttribute==null)
        {
            // not found, not specified, can't determine property descriptors.
            return new PropertyDescriptorCollection(null);
        }

        // create instance
        IEntity2 newInstance = (IEntity2)Activator.CreateInstance(typeAttribute.TypeContainedInCollection);

        // create property descriptors.
        if(base.Site==null)
        {
            return base.GetPropertyDescriptors(newInstance, typeAttribute.TypeContainedInCollection);
        }
        else
        {
            // in design mode. Because we ended up here, listAccessors is at least of length 1. Don't include collection properties.
            return base.GetPropertyDescriptors(newInstance, typeAttribute.TypeContainedInCollection, true);
        }
    }

    #region Custom EntityCollection code

    // __LLBLGENPRO_USER_CODE_REGION_START CustomEntityCollectionCode
    // __LLBLGENPRO_USER_CODE_REGION_END
    #endregion

    #region Included Code

    #endregion
}

}

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 03-Mar-2006 08:05:20   

Why not to use a UnitOfWork object?

tolga
User
Posts: 25
Joined: 26-Feb-2006
# Posted on: 03-Mar-2006 19:51:03   

I don't think I understand in what way I would use UnitOfWork to keep track of the deleted entities, so that I can pass them to the DataAccessAdapter.DeleteEntityCollection.

Could you explain?

Thanks

bclubb
User
Posts: 934
Joined: 12-Feb-2004
# Posted on: 04-Mar-2006 02:47:52   

Well instead of maintaining the deleted collection and dirty collection you could add them to the unit of work marked with being deleted or saved. Then you could commit this unit of work when the user is completed or not and leave everything the way it is. Then you don't have two collections and the unit of work will take care of which operations must happen first when call the commit.

Take a look under the Unit of Work section of the manual for some good examples and more in depth explanation.


// add the customer for a recursive save action.
uow.AddForSave(newCustomer, true);

// add this product for deletion.
uow.AddForDelete(productToDelete);

// commit all actions in one go
uow.Commit(adapter, true);

tolga
User
Posts: 25
Joined: 26-Feb-2006
# Posted on: 06-Mar-2006 02:01:21   

excellent advice. thank you