代码优先模型映射不正确
code-first models mapped incorrectly
我是 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;
}
最后评论后补充
在我看来,您想在用户和帐户之间配置一对一的关系:每个用户都有一个帐户,每个帐户都由一个用户拥有。没有帐户就没有用户,没有用户就没有帐户。
通常一对一最好使用组合配置:每个用户都有一个帐户。结果将是用户和他的帐户在一个 table 中,从而加快查询速度。此外,更容易确保没有没有帐户的用户或没有用户的帐户。更改给定用户的帐户数据也将更加容易。
在 entity framework 中,您可以这样做:
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;}
...
}
效果是帐户和用户将合二为一table。
要获取具有给定名称的用户的某些帐户属性的某些用户属性,就像:
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,
...
},
});
可能是您没有计划真正的一对一,而是
one-to-zero-or-one relation,允许某些用户还没有帐户,或者某些帐户可能没有用户。这需要两个 tables:
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;}
...
}
因为我是跟着the Entity Framework conventions,这样就足以让entity framework明白Id是主键,要由数据库来填充。不需要属性,也不流利API。如果您遵循命名约定,Entity Framework 也足够智能以检测外键:
一对多:假设一个用户有零个或多个帐户,每个帐户都属于一个用户:
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;}
...
}
由于遵循约定,entity framework 检测到您的关系。它自动知道外键。它将确保您无法在没有用户的情况下创建帐户。
补充:确保某些值是唯一的
主键始终是唯一的。您不必为此担心。
如果你希望其他项是唯一的,即这些值仍然可以更改,但是如果将它们更改为已使用的值,数据库将不会接受,你应该给它一个唯一索引注释.这是在您的 DbContext.OnModelCreating.
中完成的
假设每个 Email
在 'Accounts' 的集合中必须是唯一的。你希望能够给它一个不同的值,但是不允许给它一个已经被不同的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);
...
}
你说的是Account的email属性应该放在一个index里面。索引的名称是 "indexEmails"。索引中的所有值都应该是唯一的。
现在,每当您要添加或更新帐户时,数据库都会将电子邮件添加到索引中,并断言此电子邮件值尚未使用。
您在 SaveChanges() 期间遇到异常。
如果您希望两个值的组合是唯一的,例如组合(电子邮件、密码),您必须为这两个属性提供同名的索引注释。
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);
我是 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;
}
最后评论后补充
在我看来,您想在用户和帐户之间配置一对一的关系:每个用户都有一个帐户,每个帐户都由一个用户拥有。没有帐户就没有用户,没有用户就没有帐户。
通常一对一最好使用组合配置:每个用户都有一个帐户。结果将是用户和他的帐户在一个 table 中,从而加快查询速度。此外,更容易确保没有没有帐户的用户或没有用户的帐户。更改给定用户的帐户数据也将更加容易。
在 entity framework 中,您可以这样做:
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;}
...
}
效果是帐户和用户将合二为一table。
要获取具有给定名称的用户的某些帐户属性的某些用户属性,就像:
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,
...
},
});
可能是您没有计划真正的一对一,而是 one-to-zero-or-one relation,允许某些用户还没有帐户,或者某些帐户可能没有用户。这需要两个 tables:
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;}
...
}
因为我是跟着the Entity Framework conventions,这样就足以让entity framework明白Id是主键,要由数据库来填充。不需要属性,也不流利API。如果您遵循命名约定,Entity Framework 也足够智能以检测外键:
一对多:假设一个用户有零个或多个帐户,每个帐户都属于一个用户:
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;}
...
}
由于遵循约定,entity framework 检测到您的关系。它自动知道外键。它将确保您无法在没有用户的情况下创建帐户。
补充:确保某些值是唯一的
主键始终是唯一的。您不必为此担心。
如果你希望其他项是唯一的,即这些值仍然可以更改,但是如果将它们更改为已使用的值,数据库将不会接受,你应该给它一个唯一索引注释.这是在您的 DbContext.OnModelCreating.
中完成的假设每个 Email
在 'Accounts' 的集合中必须是唯一的。你希望能够给它一个不同的值,但是不允许给它一个已经被不同的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);
...
}
你说的是Account的email属性应该放在一个index里面。索引的名称是 "indexEmails"。索引中的所有值都应该是唯一的。
现在,每当您要添加或更新帐户时,数据库都会将电子邮件添加到索引中,并断言此电子邮件值尚未使用。
您在 SaveChanges() 期间遇到异常。
如果您希望两个值的组合是唯一的,例如组合(电子邮件、密码),您必须为这两个属性提供同名的索引注释。
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);