也许问题之一是,你立即开始考虑表和ID和外键等。如果你在对象和对象之间的关系中思考,通常会容易得多。
阅读你的照片有点困难。从我收集的文本来看,你有一个帐户的概念,显然有三种类型的帐户:普通,培训人员和健身房老板。这些帐户之间有区别吗?健身房拥有者是否拥有普通用户不具备的属性?或者它只是某种标识。
从您的绘图看来,普通用户似乎拥有用户名,密码和访问级别。培训师也有这些属性?
如果是这样,那么在正常的面向对象的设计,你会说这是一个集合:培训师,正常人和健身房业主都有一个用户名,密码和访问级别。聚合也常被称为构图。有一个小的差异,但对于这个讨论它并不重要。
而不是聚合/合成(对象为一个帐户),你也可以想到继承:对象是一个帐户:教练帐户,健身房老板帐户和普通帐户都是对象,它们都是不同种类的帐户,用户名,密码和访问级别。
在这个例子中,聚合(教练有一个帐户)或继承(教练帐户是一个帐户)没有很大的区别。通常建议是赞成继承聚合enter link description here
请注意:如果您的三个帐户之间的唯一区别是属性的值,那么请考虑将它们全部设为相同类型,并使用属性AccountType向您提供有关信息无论是普通帐户,健身房所有者帐户还是教练帐户。
如果这三者之间的唯一区别是访问级别,则不要创建帐户类型。访问级别将用作帐户类型。
到目前为止,我只谈论面向对象的设计。一旦你设计了你的对象,你可能会想到把它们放在一个数据库中。
让我们假设一个教练,一个健身房老板和一个普通人真的是不同的东西,具有不同的属性。你的班级看起来类似于:
public enum AccessLevel
{
None,
...,
FullAccess,
}
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
如果你的设计是这样的,你会发现至少你有一个健身房老板桌和一个教练桌。但是如何处理帐户。
一个解决方案是将帐户放在单独的表格中,并向培训师和健身房老板添加一些内容,这样如果您有培训师,您就可以知道帐户表中的哪个项目属于此特定培训师。
此方法通常不用于此设计。如果对象A和对象B有一对一的关系:一个A属于一个B,一个B属于一个A,那么实际上它们可以放在一个表中。如果您使用实体框架并且已经定义了上述类,则帐户属性将作为列添加到教练员表以及作为健身房所有者表的列。优点是获取关于教练员的信息更快,因为这只涉及访问一个表。
关于主键。每个表中的每个元素都应该只有一个主键。这是标识对象的属性。如果您拥有密钥,则可以非常快速地访问所有其他属性。为了让您更容易理解帐户的角色,我在原始代码中省略了ID。
但现在我们已经决定最好让培训师和健身房的老板有一个帐户,那里会有两个桌子:培训师和健身房老板。这些是唯一具有主键的元素。这些类将如下所示:
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
请注意,没有帐户表,因此帐户不需要密钥。
所以,当你需要一个外键
如果一个教练不会有一个帐户,但几个账户,也许很多帐户,通常称为帐户的集合,那么我们就无法保存该账户再次成为Trainer表中的列。我们必须将培训师的所有账户放在一张单独的表格中,并告诉每个账户所属的培训师。该帐户获得外键至他所属的培训师的主键。
在数据库方面,这被称为一对多关系:一个培训师有很多帐户。
对于实体框架的类会是这样:
public class Account
{
public int Id {Get; set;}
public int TrainerId {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> Accounts {get; set;}
}
注意,由于帐户有其表它已经得到了主键。 旁边还有一个TrainerId属性的外键。
我也添加了从DbContext派生的类来访问表。直到知道我只有带培训师的表格和带有帐户的表格。每个表都称为DbSet,DbSet的类型通知实体框架有关表中的列。
由于培训师具有虚拟帐户集合,实体框架知道培训师和帐户之间存在一对多关系,并且由于名称为TrainerId,因此实体框架知道TrainerId是主要的外键该帐户所属的培训师的密钥。
所以,如果你有一个教练的id,你得到这个教练的所有帐户使用以下的Linq语句:
int trainerId = GetMyTrainerId();
IEnumerable<Account> accountsOfTrainer = dbContext.Accounts
.Where(account => account.TrainerId == trainerId);
从帐户收集,取其中财产TrainerId等于trainerId所有记录
所以,现在你知道如何设计一个主键,并让外键指向它。
但是健身房老板呢?如果一位健身房老板只有一个账户,就让它拥有一个账户(组合)。但是,如果您的健身房老板也拥有一系列帐户,该怎么办?
如果您只是将健身房老板账户添加到账户表中,那么您会遇到麻烦。外键指向哪个Id?在培训师表或健身房所有者表中的主键?
最安全的方法是创建GymOwnersAccount和TrainersAccount。他们每个人都有自己的表与外键。一个GymOwnersAccount将有一个外键给GymOwners表,一个TrainersAccount将有一个外键给训练者表。
在这里,您也可以决定让GymOwners帐户拥有一个帐户,但似乎更自然地说,一个GymOwners帐户是一种特殊类型的帐户,因此来自帐户。
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class GymOwnerAccount : Account
{
public int Id {get; set;}
public int GymOwnerId {get; set;}
}
public class TrainerAccount : Account
{
public int Id {get; set;}
public int TrainerId {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> GymOwnerAccounts {get; set;}
public DbSet<Account> TrainerAccounts {get; set;}
}
还有其他的解决方案成为可能,例如给每个帐户的两个外键,一个健身房老板和一个培训师,在这里你总是需要一个外键设置为0可以很容易地看到,这可能导致维护问题,虽然它不给你任何好处,所以我会建议坚持用GymOwnerAccounts和TrainerAccounts单独的表。
现在我已经进入了数据库中的继承领域,您可以配置丰富的项目。一篇帮助我理解实体框架,以及如何将类,继承,组合转移到数据库的文章是Entity Framework Code First
这里有很多问题。首先是基本的[数据库设计](http://sqlmag.com/database-performance-tuning/sql-design-why-you-need-database-normalization)并找出你的实体。其次,你有安全问题。 .NET有一些很好的内置功能,如[Identity](https://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity)可以提供帮助。 –
我只是为自己做一个项目,而不是把它们放到网上。如果安全问题是关于散列密码的问题,而不是我计划的问题,但我还没有完成,因为我更专注于获得更清晰的图像,然后在实施它们之后,我会照顾其他细微差别安全等。我只想要一个粗糙的形式,我可以擦亮。 – john
好吧,从您的基本实体开始:帐户,健身房,用户,培训师。主键取决于你 - 我更喜欢[代理键](http://stackoverflow.com/questions/20052799/surrogate-key-vs-natural-key-for-ef),但你可以使用像UserName这样的自然键如果你喜欢。然后在这些表格之间建立关系,EF将完成后台工作。 –