代码优先模型映射不正确



我是SQL新手,在将一些对象映射在一起时遇到了一些麻烦。我有一个帐户对象和一个用户对象。

帐户对象使用Email作为其主键。用户对象使用Username作为其主键。

我希望 AccountModel 中的Username字段充当用户模型的外键,但它似乎不起作用。用户对象Username设置为Email字段

public class AccountModel : IAccountModel
{
public AccountModel() { }
[Key]
public string Email { get; set; }
public string Password { get; set; }
public string Username { get; set; }
public string VerifyURL { get; set; }
public bool Verified { get; set; }
public DateTime URLValidSince { get; set; }
[Required]
public virtual UserModel User { get; set; }
}
public class UserModel : IUserModel
{
[Key, ForeignKey("Account")]
public string Username { get; set; }
[Required]
public virtual AccountModel Account { get; set; }
}
public static void main() {
AccountModel acc = new AccountModel(email, password, username);
UserModel user = new UserModel(username);
acc.User = user;
user.Account = acc;
this.DBUtilites.Insert(acc);
}
public T Insert<T>(T t) where T : class {
try
{
using (DBUtilities ctx = new DBUtilities(ConfigurationManager.ConnectionStrings["DBConnectionStr"].ConnectionString))
{
DbSet<T> dbSet = ctx.Set<T>();
dbSet.Add(t);
ctx.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception Inserting Entity: " + ex);
int exceptionNo = HandleException(ex);
}
return t;
}

末尾注释后的添加

在我看来,您想在用户和帐户之间配置一对一的关系:每个用户只有一个帐户,每个帐户只由一个用户拥有。没有帐户就没有用户,也没有没有用户的帐户。

通常,一对一的最佳配置是使用组合:每个用户只有一个帐户。结果将是用户及其帐户位于一个表中,从而加快了查询速度。此外,更容易确保没有帐户的用户,或没有用户的帐户。更改给定用户的帐户数据也会更容易。

在实体框架中,您可以执行以下操作:

class Account
{
public string Email {get; set;}
public string Password {get; set;} // by the way: very unsave, prone to be hacked
...
}
class User
{
public string Name {get; set;}
public Account Account {get; set;}
...
}

效果将是帐户和用户将位于一个表中。

要获取具有给定名称的用户的某些帐户属性的某些用户属性,如下所示:

var result = myDbContext.Users
.Where(user => user.Name = ...)
.Select(user => new
{   // get only the properties you plan to use:
Name = user.Name,
...
// only if you also need some account properties:
Account = new
{
Email = user.Account.Email,
Password = user.Account.Password,
...
},
});

可能是你没有计划真正的一对一,而是 一对一关系,允许某些用户还没有帐户,或者某些帐户没有用户。这需要两个表:

class User
{
...
// every user has zero or one Account:
public virtual Account Account {get; set;}
}
class Account
{
...
// every Account belongs to zero or one User
public virtual User User  {get; set;}
}

查询将类似于我上面描述的查询,除了您需要检查 User.Account 是否为 null

最后:使用电子邮件和姓名作为主键是非常不明智的

您确定每个用户都有一个唯一的名称吗?如果用户更改了他的姓名(例如,因为其中有输入错误),它会是另一个用户吗?

切勿使用可能更改的项目作为主键。如果让数据库决定主键的值,则最有效:

class User
{
public int Id {get; set;}
...
}
class Account
{
public int Id {get; set;}
...
}

因为我遵循了实体框架约定,所以这足以让实体框架了解 Id 是主键,由数据库填充。不需要属性,也不需要流畅的API。如果遵循命名约定,实体框架也足够智能,可以检测外键:

一对多:假设一个用户有零个或多个帐户,每个帐户仅由一个用户拥有:

class User
{
public int Id {get; set;}
// every user has zero or more Accounts:
public virtual ICollection<Account> Accounts {get; set;}
...
}
class Account
{
public int Id {get; set;}
// every Account belongs to exactly one User, using foreign key:
public int UserId {get; set;}
public virtual User User {get; set;}
...
}

由于遵循以下约定,实体框架会检测您的关系。它会自动知道外键。它将确保您无法在没有用户的情况下创建帐户。

另外:确保某些值是唯一的

主键始终是唯一的。您不必担心。

如果希望其他项是唯一的,这意味着这些值仍然是可更改的,但是如果将它们更改为已使用的值,数据库将不接受它,则应为其提供唯一的索引注释。这是在您的 DbContext.OnModelCreation 中完成的。

假设每个Email在"帐户"集合中必须是唯一的。您希望能够为其提供不同的值,但不允许为其提供已被其他Account使用的值。

在您的 DbContext 中:

protected virtual OnModelCreating(DbModelBuilder modelBuilder)
{
// From table Accounts, give property Email a unique index annotation:
const string indexName = "indexEmails"
var indexAttribute  = new IndexAttribute(indexName, 0) {IsUnique = true};
var indexAnnotation = new IndexAnnotation(indexAttribute);
propertyEmail = modelBuilder.Entity<Account>()
.Property(account => account.Email)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
...
}

您所做的是说帐户的电子邮件属性应放在索引中。索引的名称是"索引电子邮件"。索引中的所有值都应该是唯一的。

现在,每当您想要添加或更新帐户时,数据库都希望将电子邮件添加到索引中,并断言尚未使用此电子邮件值。

您在保存更改()期间出现异常。

如果您希望两个值的组合是唯一的,例如组合(电子邮件、密码),您必须为这两个属性提供具有相同名称的索引注释。

var entityAccount = modelBuilder.Entity<Account>();
var propertyEmail = entityAccount.Property(account => account.Email);
var propertyPassword = entityAccount.Property(account => account.Password);
// indes: first order by (0) email, then by (1) password
const string indexName = "IX_MyIndex");
var indexAttribute0  = new IndexAttribute(indexName, 0) {IsUnique = true};
var indexAnnotation0 = new IndexAnnotation(indexAttribute);
propertyEmail.HasUniqueIndexAcnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
var indexAttribute1  = new IndexAttribute(indexName, 1) {IsUnique = true};
var indexAnnotation1 = new IndexAnnotation(indexAttribute);
propertyPassword.HasUniqueIndexAcnnotation(IndexAnnotation.AnnotationName, indexAnnotation);

最新更新