Tôi nhận ra câu trả lời là một năm cũ, nhưng tôi thấy nó không đầy đủ. Trong tâm trí của tôi, một bảng tự tham khảo được sử dụng để đại diện cho một chiều sâu tùy ý.
Ví dụ, hãy xem xét cấu trúc sau:
/*
* earth
* europe
* germany
* ireland
* belfast
* dublin
* south america
* brazil
* rio de janeiro
* chile
* argentina
*
*/
Câu trả lời không giải quyết làm thế nào để xóa trái đất, hoặc châu Âu, từ cấu trúc bên trên.
Tôi gửi mã sau đây làm phương án thay thế (sửa đổi câu trả lời được cung cấp bởi Slauma, người đã làm tốt công việc btw).
Trong lớp MyContext, thêm các phương pháp sau:
public void DeleteMyEntity(MyEntity entity)
{
var target = MyEntities
.Include(x => x.Children)
.FirstOrDefault(x => x.Id == entity.Id);
RecursiveDelete(target);
SaveChanges();
}
private void RecursiveDelete(MyEntity parent)
{
if (parent.Children != null)
{
var children = MyEntities
.Include(x => x.Children)
.Where(x => x.ParentId == parent.Id);
foreach (var child in children)
{
RecursiveDelete(child);
}
}
MyEntities.Remove(parent);
}
tôi cư dữ liệu bằng mã đầu tiên với lớp sau:
public class TestObjectGraph
{
public MyEntity RootEntity()
{
var root = new MyEntity
{
Name = "Earth",
Children =
new List<MyEntity>
{
new MyEntity
{
Name = "Europe",
Children =
new List<MyEntity>
{
new MyEntity {Name = "Germany"},
new MyEntity
{
Name = "Ireland",
Children =
new List<MyEntity>
{
new MyEntity {Name = "Dublin"},
new MyEntity {Name = "Belfast"}
}
}
}
},
new MyEntity
{
Name = "South America",
Children =
new List<MyEntity>
{
new MyEntity
{
Name = "Brazil",
Children = new List<MyEntity>
{
new MyEntity {Name = "Rio de Janeiro"}
}
},
new MyEntity {Name = "Chile"},
new MyEntity {Name = "Argentina"}
}
}
}
};
return root;
}
}
mà tôi lưu vào cơ sở dữ liệu của tôi với những điều sau đây mã:
ctx.MyEntities.Add(new TestObjectGraph().RootEntity());
sau đó gọi các lần xóa như sau:
using (var ctx = new MyContext())
{
var parent = ctx.MyEntities
.Include(e => e.Children)
.FirstOrDefault();
var deleteme = parent.Children.First();
ctx.DeleteMyEntity(deleteme);
}
mà kết quả trong cơ sở dữ liệu của tôi bây giờ có một cấu trúc như sau:
/*
* earth
* south america
* brazil
* rio de janeiro
* chile
* argentina
*
*/
nơi europe và tất cả các con của nó sẽ bị xóa.
ở trên, tôi chỉ định con đầu tiên của nút gốc, để chứng minh rằng sử dụng mã của tôi, bạn có thể đệ quy xóa một nút và tất cả các con của nó từ bất kỳ đâu trong cấu trúc phân cấp.
nếu bạn muốn kiểm tra xóa everyting, bạn chỉ có thể sửa đổi dòng như thế này:
ctx.DeleteMyEntity(parent);
hoặc bất cứ nút bạn muốn trong cây.
rõ ràng, tôi sẽ không nhận được tiền thưởng, nhưng hy vọng bài đăng của tôi sẽ giúp ai đó tìm kiếm giải pháp hoạt động cho các thực thể tự tham chiếu có chiều sâu tùy ý.
Đây là nguồn gốc đầy đủ, mà là một phiên bản sửa đổi của mã Slauma từ câu trả lời chọn:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace EFSelfReference
{
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public MyEntity Parent { get; set; }
public ICollection<MyEntity> Children { get; set; }
}
public class MyContext : DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasOptional(e => e.Parent)
.WithMany(e => e.Children)
.HasForeignKey(e => e.ParentId);
}
public void DeleteMyEntity(MyEntity entity)
{
var target = MyEntities
.Include(x => x.Children)
.FirstOrDefault(x => x.Id == entity.Id);
RecursiveDelete(target);
SaveChanges();
}
private void RecursiveDelete(MyEntity parent)
{
if (parent.Children != null)
{
var children = MyEntities
.Include(x => x.Children)
.Where(x => x.ParentId == parent.Id);
foreach (var child in children)
{
RecursiveDelete(child);
}
}
MyEntities.Remove(parent);
}
}
public class TestObjectGraph
{
public MyEntity RootEntity()
{
var root = new MyEntity
{
Name = "Earth",
Children =
new List<MyEntity>
{
new MyEntity
{
Name = "Europe",
Children =
new List<MyEntity>
{
new MyEntity {Name = "Germany"},
new MyEntity
{
Name = "Ireland",
Children =
new List<MyEntity>
{
new MyEntity {Name = "Dublin"},
new MyEntity {Name = "Belfast"}
}
}
}
},
new MyEntity
{
Name = "South America",
Children =
new List<MyEntity>
{
new MyEntity
{
Name = "Brazil",
Children = new List<MyEntity>
{
new MyEntity {Name = "Rio de Janeiro"}
}
},
new MyEntity {Name = "Chile"},
new MyEntity {Name = "Argentina"}
}
}
}
};
return root;
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<MyContext>(
new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
ctx.Database.Initialize(false);
ctx.MyEntities.Add(new TestObjectGraph().RootEntity());
ctx.SaveChanges();
}
using (var ctx = new MyContext())
{
var parent = ctx.MyEntities
.Include(e => e.Children)
.FirstOrDefault();
var deleteme = parent.Children.First();
ctx.DeleteMyEntity(deleteme);
}
Console.WriteLine("Completed....");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
}
Bạn có thể sử dụng một thác xóa đây? Nếu bạn đang sử dụng mã di chuyển đầu tiên, bạn có thể đặt cascade = true khi bảng được tạo, nếu không bạn có thể phải cập nhật nó thông qua di chuyển khác hoặc thông qua cơ sở dữ liệu. Tôi nghĩ EF sẽ xử lý nếu nó có thể xếp hàng. –
Ý tưởng hay. Các DBA của chúng tôi không muốn kích hoạt thác cho toàn bộ DB, nhưng chúng ta có thể thực hiện nó ngay trên tham chiếu FK này. Đó là những gì tôi vừa thảo luận với một DBA. :) Mặc dù tôi vẫn đang tìm kiếm các đề xuất/ý tưởng khác có lẽ nằm trong phạm vi mã liên quan đến EF. Cập nhật – Kon
: Không thể thêm thác khi xóa vào FK trong tham chiếu tự trong bảng, do lỗi chu trình. – Kon