代码优先模型映射不正确

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);