我想为类似的表做继承。
例如,假设我们有两个表:Teachers
和Students
这两个表都可以从基类Human
派生。
是否可以编写常见任务并避免使用EntityFramework重复代码?这样的函数适用于DbSet<Student>
和DbSet<Teacher>
:
void IncreaseAge(DbSet<Human> humans, int id)
{
//
}
更重要的是db的泛型添加函数,伪代码:
void AddHuman({...}, string name, int age)
{
// add a human to related table
}
任何帮助将不胜感激。
答案 0 :(得分:3)
将扩展名与通用参数一起使用:
void IncreaseAge<T>(DbSet<T> entities, int id) where T: Human
{
var current = entities.Find(id);
current.Age++;
// SaveChanges() in the context
}
如果您的学生从人类继承Age属性。这段代码应该可以正常运行。
此外,您可以对添加
应用相同的技术void Insert<T>(DbSet<T> entities, string name, int age) where T: new(), Human
{
entities.Add(new T{ Name = name, Age = age });
// SaveChanges() in the context
}
希望这有帮助!
答案 1 :(得分:1)
DbSet代表所有实体。您通常不会将其传递给某个功能。而是在DbContext中考虑这样的方法:
public void IncreaseAge(IQueryable<Human> humans)
{
foreach( var h in humans)
{
h.Age++;
}
SaveChanges();
}
然后,您可以传递一个查询,指定您要操作的一组教师或学生。 EG:
db.IncreaseAge(db.Teachers.Where(t => t.Age == 47));
答案 2 :(得分:0)
我猜你在Entity Framework中使用继承时已经读过various strategies。关于实体框架的好处是它隐藏了使用的继承策略。
由于链接中描述了三种继承策略,因此无需描述为此所需的流畅API。我只会写你最终会上的课程,以及如何对学生,教师和普通基础人员进行查询。
此外,我将描述正确选择正确的继承策略所需的一些注意事项。
所以你有Student
和Teachers
,都来自Person
。
abstract class Person
{
public string Name {get; set}
public Gender Gender {get; set;}
public DateTime Birthday {get; set;}
}
public class Teacher : Person
{
public int Id {get; set;}
...
}
public class Student : Person
{
public int Id {get; set;}
...
}
和DbContext:
public class MyDbContext : DbContext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
}
现在,只要您拥有教师或学生的IQueryable,您就可以使用Person的所有属性
var maleTeachers = myDbContext.Teachers
.Where(teacher => teacher.Gender == Gender.Male);
var youngStudents = myDbcontext.Students
.Where(student => student.Birthday > new Datetime(2000, 1, 1);
如果您需要查询所有人员,则必须连接学生和教师。在婴儿步骤:
IQueryable teachers = myDbcontext.Teachers.Cast(); IQueryable students = myDbContext.Students.Cast(); IQueryable allPersons = teachers.Concat(学生); var result = allPersons.Where(person =&gt; ...) 。选择(person =&gt; ...) ......等等。
当然,这可以在一个声明中完成。
使用哪种继承策略?
在决定继承策略时,请记住您最常做的查询:
如果你经常做第一次,那么考虑Table per concrete class (TPC).你将有两张桌子,一张给学生,一张给老师。 Person属性位于同一个表中。
优点是,如果你要求“学生......”,只涉及一张表。没有必要加入。
缺点是如果你要求“人......”,教师和学生表需要连接。
如果您要更频繁地查询人员,请考虑creating Table per Type (TPT)。结果将是三张外键表:教师,人员,学生。在询问人员时,只涉及一张桌子。但是,在要求教师时,我们总是需要加入两个表格。
即使您选择了最佳的继承策略,因为这些是您最常执行的查询,有时您可能需要执行其他类型的查询。
每个具体课程的TPC表两个表:一个用于学生,一个用于教师,因为您主要查询学生...或教师......没有人员表。
如果你不得不偶尔进行Person查询,你必须连接这两个序列。在婴儿步骤:
IQueryable<Person> teachers = myDbcontext.Teachers.Cast<Person>();
IQueryable<Person> students = myDbContext.Students.Cast<Person>();
IQueryable<Person> allPersons = teachers.Concat(students);
var result = allPersons.Where(person => ...)
.Select(person => ...)
... etc
当然,这可以在一个声明中完成。
如果你不得不经常这样做,可以考虑为你的DbContext类添加一个属性:
class MyDbcontext : Dbcontext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
public IQueryable<Person> Persons
{
get
{
return this.Teachers.Cast<Person>()
.Concat(this.Students.Cast<Person>());
}
}
}
用法将是:
using (var myDbContext = new MyDbContext(...))
{
IQueryable<Person> females = myDbcontext.Persons
.Where(person => person.Gender == Gender.Female);
}
如果您不想污染您的Dbcontext,请考虑创建一个相同的扩展功能。见extension functions demystified
static class MyDbContextExtensions
{
IQueryable<Person> Persons(this MyDbContext dbContext)
{
return dbContext.Teachers.Cast<Person>()
.Concat(dbContext.Students.Cast<Person>());
}
}
TPT:每种类型的表格三个表格,学生,教师,人员。内部是学生对人的外键
您可以使用DbSet直接查询人员。如果您只想要教师...,只需访问DbSet。只要您使用其中一个继承的Person属性,Entity框架就会自动为您执行连接。你不会在代码中看到这一点。
所以要注意你选择的继承策略