【问题标题】:cascading delete using recursive method使用递归方法级联删除
【发布时间】:2018-01-13 02:35:51
【问题描述】:

我需要一种方法来删除用户及其在其他表中的所有约束。我知道 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 ICollections。

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]>;

但我无法获得正确的演员阵容。

如果这是完全错误的,任何提示都会让我找到正确的方向。

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 它只是一个本地测试应用程序,我需要删除所有测试用户和它附带的东西。它并不打算在日常用例中发布和使用。此外,还有很多关系和表,我想不出在数据库方面这样做的方法。
    【解决方案2】:

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

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

    注意错字。我在手机上写下了答案。

    【讨论】:

      【解决方案3】:

      如果有人需要我正在寻找的东西。该类获取一个对象并找到所有 ICollection&lt;T&gt; 类型的关系并递归遍历它们并删除基本子项。

      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();
      }
      

      【讨论】:

        猜你喜欢
        • 2014-09-05
        • 2014-08-23
        • 1970-01-01
        • 1970-01-01
        • 2017-11-13
        • 2018-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多