代码优先模型映射不正确

时间:2018-01-10 02:23:31

标签: c# entity-framework ef-code-first

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

帐户对象使用 import UIKit import Firebase class UsersTableViewController: UITableViewController { var user = [User]() override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func getusers() { let userID = Auth.auth().currentUser?.uid let rootRef = Database.database().reference() let query = rootRef.child("users").queryOrdered(byChild: "fullname") query.observe(.value) { (snapshot) in for child in snapshot.children.allObjects as! [DataSnapshot] { if let value = child.value as? NSDictionary { let userToShow = User() let fullname = value["fullname"] as? String ?? "Name not found" let uid = value["uid"] as? String ?? "uid not found" userToShow.fullname = fullname userToShow.userID = uid self.user.append(userToShow) DispatchQueue.main.async { self.tableView.reloadData() } } } } } override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return user.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! TableViewCell cell.nameLabel.text = self.user[indexPath.row].fullname cell.userID = self.user[indexPath.row].userID cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!) return cell } } extension UIImageView { @objc func downloadImage(from imgURL: String!) { let url = URLRequest(url: URL(string: imgURL)!) let task = URLSession.shared.dataTask(with: url) { (data, response, error) in if error != nil { print(error!) return } DispatchQueue.main.async { self.image = UIImage(data: data!) } } task.resume() }} 作为主键。用户对象使用Email作为主键。

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

Email

1 个答案:

答案 0 :(得分:1)

在最后评论后添加

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

通常情况下,使用Composition最好配置一对一:每个用户只有一个帐户。结果是用户和他的帐户在一个表中,从而加快了查询速度。此外,更容易确保没有没有帐户的用户或没有用户的帐户。更改给定用户的帐户数据也会更容易。

在实体框架中,您可以这样做:

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,
            ...
        },
     });

可能你没有计划真正的一对一,但是a one-to-zero-or-one relation,允许某些用户还没有帐户,或者某些帐户没有用户。这需要两个表:

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是否为空

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

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

永远不要使用可能会更改为主键的项目。如果让数据库决定主键的值,则效率最高:

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

因为我跟着the Entity Framework conventions,这足以让实体框架理解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.OnModelCreating。

中完成的

假设每个Email在&#39;帐户&#39;的集合中必须是唯一的。您希望能够为其提供不同的值,但不允许为其提供已由其他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);
     ...
}

您所做的就是说您应该将帐户的电子邮件属性放入索引中。索引的名称是&#34; indexEmails&#34;。索引中的所有值都应该是唯一的。

现在,无论何时您想添加或更新帐户,数据库都希望将电子邮件添加到索引中,并声明此电子邮件值尚未使用。

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