Entity Framework code first learning diary (8)-one-to-one relationship

Source: Internet
Author: User

Based on the previous two diaries, I believe you have learned how Entity Framework code first can infer and establish one-to-many and many-to-many relationships between tables in the database based on the dependency between classes. In this diary, We will detail how Entity Framework code first establishes a one-to-one relationship in the database.

When introducing the one-to-many relationship and the many-to-many relationship, you should have noticed that the definitions of the two classes that have dependency relations include the instances of the other party or the set of instances, entity Framework code first will automatically infer the corresponding database relationship. Does this method also apply to one-to-one relationships? Let's first make an experiment.

Assume that our order system now needs to store the bank account information of each customer. Obviously, in our order system, bank accounts are not the focus of our attention. I only need to save the account number, account opening bank, and account name, it can be seen that the bank account is only a value object here.

We need to define the bank account class:

public class BankAccount    {        public string AccountNumber { get; set; }        public DateTime CreatedDate { get; set; }        public string BankName { get; set; }        public string AccountName { get; set; }    }

We also need to include a bank account instance in the customer class.

 

public class Customer    {        public string IDCardNumber { get; set; }        public string CustomerName { get; set; }        public string Gender { get; set; }        public Address Address { get; set; }        public string PhoneNumber { get; set; }        public BankAccount Account { get; set; }    }

Let's write a unit test program to see if Entity Framework code first does not automatically create a one-to-one relationship in the database based on the dependency between the two classes.

 

[TestMethod]        public void CanAddCustomerWithBankAccount()        {            OrderSystemContext unitOfWork = new OrderSystemContext();            CustomerRepository repository = new CustomerRepository(unitOfWork);            Customer newCustomer = new Customer() { IDCardNumber = "120104198106072518", CustomerName = "Alex", Gender = "M", PhoneNumber = "test" };            Address customerAddress = new Address { Country = "China", Province = "Tianjin", City = "Tianjin", StreetAddress = "Crown Plaza", ZipCode = "300308" };            BankAccount account = new BankAccount { AccountNumber = "2012001001", BankName = "ICBC", AccountName = "Alex", CreatedDate = DateTime.Parse("2012-1-21") };            newCustomer.Address = customerAddress;            newCustomer.Account = account;            repository.AddNewCustomer(newCustomer);            unitOfWork.CommitChanges();        }

 

Run our unit test program and the program throws an exception:

Test method EntityFramework.CodeFirst.Demo1.UnitTest.CustomerRepositoryUnitTest.CanAddCustomerWithBankAccount threw exception: System.Data.DataException: An exception occurred while initializing the database. See the InnerException for details. ---> System.Data.Entity.Infrastructure.DbUpdateException: Null value for non-nullable member. Member: 'Account'. ---> System.Data.UpdateException: Null value for non-nullable member. Member: 'Account'.

 

Why? Because Entity Framework code first cannot infer and establish a one-to-one relationship based on the dependency between classes, it cannot be clear which of the two dependency classes is the primary table, which is a sub-table and which table should the foreign key be created in. In a one-to-multiple relationship, it is very easy to distinguish between the master table and the sub-Table. Which class contains another instance set is the master table. Many-to-many relationships are created by connecting tables. You do not need to distinguish between the master table and sub-table. However, this is a problem in the one-to-one relationship.

To make Entity Framework code first deduce and establish a one-to-one relationship based on the dependency between classes, you must help it to tell it which table is the master table and which table is the sub-table.

Assume that a bank account must have a corresponding customer, but the customer may not have a bank account, and because the bank account is a value object, there is no need to include a customer instance.

Because the definition of the bank account class does not contain the instance of the customer class, we need to set this one-to-one relationship in the configuration method of the customer class.

public class CustomerEntityConfiguration:EntityTypeConfiguration<Customer>    {        public CustomerEntityConfiguration()        {            HasKey(c => c.IDCardNumber).Property(c => c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);            this.Property(c => c.IDCardNumber).HasMaxLength(20);            this.Property(c => c.CustomerName).IsRequired().HasMaxLength(50);            this.Property(c => c.Gender).IsRequired().HasMaxLength(1);            this.Property(c => c.PhoneNumber).HasMaxLength(20);            this.HasOptional(c => c.Account).WithOptionalDependent();        }    }

Hasoptional in the customer class means that the customer class can have or have no bank account. When we use hasoptional to specify the relationship between the customer and the bankaccount class, Entity Framework code first requires us to specify the dependency between them. In this case, intelliisense allows you to select witexceptionaldependent and witexceptionalprincipal between the two methods. If you select witexceptionaldependent, it indicates that the customer table has a foreign key pointing to the bankaccount table's primary key. If you choose witexceptionalprincipal, bankaccount has a foreign key pointing to the customer table.

Execute our unit test and no error will be reported this time. Then we open SQL Server and find that Entity Framework code first maps to the correct one-to-one relationship.

 

As you can see, the foreign key in the customer table can be empty because hasoptional is used. If we need a foreign key in a one-to-one relationship, we need to use hasrequired.

HasRequired(c => c.Account).WithRequiredDependent();

When hasrequired is used, intelliisense allows you to select between withrequireddependent and withrequiredprinciple. The dependent and principle here are used to determine the table in which the primary key is located.

However, it is strange that the Entity Framework Version I used is 4.1. When I executed my test program, I found a very strange thing. Code first does not create a foreign key as hasoptional does, but changes the customer's primary key to a foreign key pointing to the bankaccount table at the same time:

 

This is certainly not the expected result, because code first will save accountnumber as idcardnumber. I think this may be a bug in ef4.1.

However, we can define the correct foreign key by specifying the foreign key name we have learned before.

HasRequired(c => c.Account).WithRequiredDependent().Map(c => c.MapKey("AccountId")); 

The correct result is returned when we run our test program again:

 

As I have introduced how to delete cascading keys and change the foreign key name in the previous two diaries, I will not repeat them in this diary. Our next diary will explore how to map the inheritance relationships between classes to databases.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.