使用递归方法级联删除

时间:2017-08-05 06:18:05

标签: c# sql-server generics recursion ef-power-tools

我需要一种方法来删除用户及其他表中的所有约束。我知道在sql server中级联删除但由于某些原因我不能使用它。

假设用户有多个订单,每个订单都有一些产品。所以我在方法中发送用户,它找到订单,在foreach循环中它进入该顺序,依此类推。

所以我要编写一个递归执行此操作的方法;它必须接收一个对象并找到它的所有关系并经历所有这些关系。

我首先使用EF电动工具反向工程代码从数据库生成这些代码。这是我的班级:

public partial class myDbContext: DbContext
{
    ...

    public DbSet<Users> Users{ get; set; }
    public DbSet<Orders> Orders{ get; set; }
    public DbSet<Products> Products{ get; set; }
    public DbSet<OrderProducts> OrderProducts{ get; set; }

    ...
}
public partial class Users
{
    public int UserID { get; set; }
    public string Username { get; set; }
    public virtual ICollection<Orders> Orders{ get; set; }
}
public partial class Orders
{
    public int OrderID { get; set; }
    public virtual Users users { get; set; }
    public virtual ICollection<OrderProducts> OPs { get; set; }
}
public partial class OrderProducts
{
    public int OPID { get; set; }
    public virtual Orders orders { get; set; }
    public virtual Product products { get; set; }
}

使用此方法,我可以在用户对象中找到所有virtual ICollection

private void DeleteObjectAndChildren(object parent) 
    {
        using (var ctx = new myDbContext())
        {
            Type t = parent.GetType();

            //these are all virtual properties of parent
            var properties = parent.GetType().GetProperties().Where(p => p.GetGetMethod().IsVirtual);
            foreach (var p in properties)
            {
                var collectionType = p.PropertyType.GetGenericArguments();
                //collectionType[0] gives me the T type in ICollection<T>

                //what to do next?
            }
        }
    }

使用collectionType[0]我看到它是Orders,我必须有这样的东西才能查询:

var childType = ctx.Set<collectionType[0]>;

但我无法获得正确的演员和所有。

如果这是完全错误的,任何提示都会受到赞赏,让我朝着正确的方向前进。

3 个答案:

答案 0 :(得分:0)

这完全不合适。当您应该首先使用这些用户映射的关系模型时,您正在执行数据库关系操作。

您的应用程序最适合调用存储过程,其中数据库语言可以映射所涉及的表并删除用户及其对象的正确行。

与您的dba和设计这些表的开发人员一起工作,并将数据库操作留给数据库,并将框架操作留给框架。

答案 1 :(得分:0)

来自@clifton_h的回答对我来说是对的。但是如果你想使用你的方法,你可以用它来返回一个包含的字符串。用于查找和删除对象。

var user = users.Include("Orders.OrdersProducts");
ctx.Delete(user);
ctx.SaveChanges();

留意拼写错误。我在手机上写了答案。

答案 2 :(得分:0)

如果有人需要我想要的东西。此类获取一个对象并查找与ICollection<T>类型的所有关系,并以递归方式遍历它们并删除基本子项。

public class RemoveEntityAndAllRelations
{
    private DbContext _context;

    public void SetContext(DbContext ctx)
    {
        _context = ctx;
    }

    public void RemoveChildren(object parent)
    {
        Type obj = parent.GetType();

        MethodInfo method = typeof(RemoveUserAndAllRelations).GetMethod("GetICollections");
        MethodInfo generic = method.MakeGenericMethod(obj);

        var properties = (PropertyInfo[])generic.Invoke(this, null);
        if (properties.Any())
        {
            foreach (PropertyInfo propertyInfo in properties)
            {
                object child = propertyInfo.GetValue(parent, null);
                RemoveChildren(child);
            }

            try
            {
                dynamic dd = parent;
                _context.Entry(dd[0]).State = EntityState.Deleted;
                _context.SaveChanges();
            }
            catch (Exception)
            {
                  //do what you like
            }
        }
        else
        {
            try
            {
                dynamic dd = parent;
                _context.Entry(dd[0]).State = EntityState.Deleted;
                _context.SaveChanges();
            }
            catch (Exception)
            {
                  //do what you like
            }
        }
    }
    public PropertyInfo[] GetICollections<TEntity>() where TEntity : class
    {
        return typeof(TEntity).GetProperties().Where(m =>
            m.PropertyType.IsGenericType &&
            m.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
        ).ToArray();
    }
}

并像

一样使用它
using (var ctx = new myDbContext())
{
    var user = ctx.Users.FirstOrDefault(x => x.UserID == userId);
    RemoveEntityAndAllRelations controller = new RemoveUserAndAllRelations();

    controller.SetContext(ctx);
    controller.RemoveChildren(user);

    ctx.Core_Users.Remove(user);
    ctx.SaveChanges();
}