Pregunta

I have this scenario: I have these classes:

public class A
{
   public int Id {get;set;}
   public virtual ICollection<B> bCollection {get; set; }
}

public class B
{
   public int Id {get;set;}

}

public class C1 : BaseClass1
{
   public int Id{get;set;}
   public virtual B B{get;set;}
}

public class C2 : BaseClass2
{
   public int Id {get;set;}
   public virtual B B {get;set;}
}

...
 public class C100 : BaseClass100
{
   public int Id {get;set;}
   public virtual B B {get;set;}
}

class A has collection of class B and class Ci have one class B and different base classes. when in class A collection there is only B that Ci not reference it, I can delete class A and all the B collection also deleted(cascade delete). But when in class A collection there is B that classes Ci has reference to it I can't delete class A instance...

My expected behavior:

Class A will be deleted and all the B collection that class A has, and if Ci has reference to some of the B in the collection it will be null in the end of the delete.( class Ci intances will not be deleted!), also I don't want to iterate throw all my Ci class to see if it has reference to the B collection that need to be deleted.

I don't want this code:

MyDbContext db=new MyDbContext();
Hash<int> bCollectionToDelete=GetBCollectionToDeleteHashSet();
var C1_db_collection= db.C1Collection.ToList();
foreach(var c1Item in C1Collection)
{
    if(bCollectionToDelete.Contains(c1Item.refrenceIdToB)
    {
       c1Item.refrenceIdToB=null;
    }
}

 var C2_db_collection= db.C2Collection.ToList();
foreach(var c2Item in C1Collection)
{
    if(bCollectionToDelete.Contains(c2Item.refrenceIdToB)
    {
       c2Item.refrenceIdToB=null;
    }
}

...
 var C100_db_collection= db.C100Collection.ToList();
foreach(var c100Item in C100Collection)
{
    if(bCollectionToDelete.Contains(c100Item.refrenceIdToB)
    {
       c100Item.refrenceIdToB=null;
    }
}

someone know how to achieve it?

¿Fue útil?

Solución

This may be a case where you write a stored procedure to handle your cascading delete logic, and the EF code just makes a single call to the 'DeleteMyObject' sp and then your ef code will need to refresh its context to reload the changes immediately after.

You will pay a slight performance penalty to refresh your context, but if I had to guess moving the logic to an sp, and the performance gain of doing inter-related deletes there, will more than offset the small performance penalty of having to refresh.

Not sure if SP's are an option for you, but they are there to use when you need them.

EDIT:

Very simplified example of stored procedure that accepts the primary key of a table 'MainTable' and then deletes any related records, if they exists, from 3 'ChildTables'. You actual logic may be a bit more complicated:

CREATE PROCEDURE DeleteMyObject @Id INT
as
   BEGIN TRAN
     delete from ChildTable1 WHERE ParentId=@Id
     delete from ChildTable2 WHERE ParentId=@Id
     delete from ChildTable3 WHERE ParentId=@Id
     delete from MainTable WHERE ID=@Id

   COMMIT TRAN

Example of calling SP that is not mapped into your EF model:

SqlParameter param1 = new SqlParameter("@Id", 12345); context.Database.ExecuteSqlCommand("DeleteMyObject @Id",param1);

Further reading: http://msdn.microsoft.com/en-us/data/gg699321.aspx

Otros consejos

Instead of searching in all C Classes you could let B remove him self

by using an interface

public interface IRemoveB
{
    void RemoveB();
}

public class C1 : BaseClass1, IDeleteB
{
    public int Id { get; set; }
    public virtual B B { get; set; }

    #region IDeleteB Member

    public void RemoveB()
    {
        this.B = null;
    }

    #endregion
}


public class B
{
    public int Id { get; set; }
    public ICollection<IDeleteB>  list{ get; set; }

}
public class A
{
    public int Id { get; set; }
    public virtual ICollection<B> bCollection { get; set; }

    public void prepareForDelete()
    {
        foreach (var item in bCollection)
        {
            foreach (var deref in item.list)
            {
                deref.RemoveB();
            }
        }
    }
}

by using an delegate

public class A
{
    public int Id { get; set; }

    public virtual ICollection<B> bCollection { get; set; }

    public void prepareForDelete()
    {
        foreach (var item in bCollection)
        {
            item.RemoveMe();
        }
    }
}


public class B
{
    public int Id { get; set; }

    public event EventHandler Remove;

    public void RemoveMe()
    {
        EventHandler removeHandler = Remove;
        if (removeHandler != null)
        {
            removeHandler(this, EventArgs.Empty);
        }
    }
}
public class C2 : BaseClass2
{
    public int Id { get; set; }

    private B internB;

    // maybe you need an otherform to set B because of your virtual 
    public B B
    {
        get { return internB; }
        set
        {
            if (internB != value)
                if (internB != null)
                    internB.Remove -= this.RemoveB;
                else if(value != null)
                    value.Remove += this.RemoveB;
            internB = value;
        }
    }

    public void RemoveB(object sender, EventArgs args)
    {
        internB.Remove -= this.RemoveB;
        B = null;

    }
}

Edit:

to Answer your Question's

You mean thet every ci inherit from the same interface?
Yes exactly. Here are more information about Interface

and if yes ICollection<IDeleteB> is stored in the database?
No it only exist in your program. Think of it like a temporary 1:N referenztable

and in the delegate example how it is store in the database?
No it's basically the same think like the Collection

and this only work if B in A == B in C which does mean it's the same instance of the Class B

you can set the option of the delete cascade to true for each class Ci in the method OnModelCreating by adding the following

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // for each class Ci write the following
    modelBuilder.Entity<Ci>()
            .HasRequired(t=>T.B)
            .WithMany()
            .WillCascadeOnDelete(true);
}

by this the delete will delete the Ci for you if exists with the B class hope it will help you

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top