none
Need help / advise with EF 4.2, Code-First, Inheritance and Relationship RRS feed

  • Question

  • Hi,
    I've been using LINQ to SQL and EF Database First for a while now. But want to try Code-First using POCO and would like to ask Guru's to advise on how to properly (best practices) create classes and mappings for following classes.
     
    public class BusinessEntity
    {
        public Int64 BusinessEntityId { get; set; }
        public ICollection<Address> Addresses  { get; set; }
    }
        
    public class Address
    {
        public Int64 AddressId { get; set; }
        public string AddressLine1 { get; set; }
        public SomeEnum AddressType { get; set; }
    }
    
    public class BusinessEntityAddress
    {
        public BusinessEntity BusinessEntity { get; set; }
        public DateTime EffectiveDate { get; set; }
        public Address Address { get; set; }
    }
    
    public class Person : BusinessEntity
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    

    Thanks!

    Friday, November 25, 2011 6:26 PM

Answers

  • Hi,

    I think you should redesign this model. You have 4 classes but I cant see the need for this complexity.

    Why do you need a main object BusinessEntityAdress that holds a single Adress Entity and a BusinessEntity that holds a List of Adresses? You want to use inheritance, so I created a model that looks similiar to yours. Enums will be supported ( I didnt get it to work with EF 4.2), thats why the Type Enum is a notMapped attribute that encapsulates the enumeration.

    The following console app shows the usage of the model. It creates an Adresses and a People table in database.

    HTH

    Holger

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication1
    {
        using System.ComponentModel.DataAnnotations;
    
        using System.Data.Entity;
    
        public enum AddressType
        {
            Private = 1,
            Work = 2
        }
    
        public class Address
        {
            public int Id { get; set; }
            public string AddressLine1 { get; set; }
            public int Type { get; set; }
    
            [NotMapped]
            public AddressType AddressType
            {
                get
                {
                    return (AddressType)Type;
                }
                set
                {
                    Type = (int)value;
                }
            }
    
        }
    
        public class BusinessEntity
        {
            public BusinessEntity()
            {
                Addresses = new List<Address>();
            }
    
            public int Id { get; set; }
            public DateTime EffectiveDate { get; set; }
            public virtual ICollection<Address> Addresses { get; set; }
        }
    
        public class Person : BusinessEntity
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }
    
    
        public class UserContext : DbContext
        {
            public DbSet<Person> Persons { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Person>().HasMany(p => p.Addresses);
                base.OnModelCreating(modelBuilder);
            }
        }
    
        public class UserContextInitalizer : DropCreateDatabaseAlways<UserContext>
        {
            protected override void Seed(UserContext context)
            {
                var p1 = new Person() { FirstName = "John", LastName = "Doe", EffectiveDate = DateTime.Now };
    
                var adr1 = new Address() { AddressLine1 = "Wallstret 1", AddressType = AddressType.Work };
                var adr2 = new Address() { AddressLine1 = "Broadway 5", AddressType = AddressType.Private };
    
                p1.Addresses.Add(adr1);
                p1.Addresses.Add(adr2);
    
                context.Persons.Add(p1);
                context.SaveChanges();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Database.SetInitializer(new UserContextInitalizer());
                using (var ctx = new UserContext())
                {
                    var p = ctx.Persons.First();
                    Console.WriteLine("Name :" + p.FirstName + " " + p.LastName);
                    foreach (var adr in p.Addresses)
                        Console.WriteLine(adr.AddressType.ToString() + ": " + adr.AddressLine1);
                    Console.ReadKey();
                }
            }
        }
    }
    
    

    The output shous following text:

     

    Name :John Doe
    Work: Wallstret 1
    Private: Broadway 5

    app.config:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <connectionStrings>
        <add name="UserContext"   providerName="System.Data.SqlClient"

    connectionString="data source=localhost;initial catalog=MyContext;persist security info=True;Trusted_Connection=True;

    MultipleActiveResultSets=True;Integrated Security=SSPI" />
      </connectionStrings>
    </configuration>

    Friday, November 25, 2011 7:40 PM

All replies

  • Hi,

    I think you should redesign this model. You have 4 classes but I cant see the need for this complexity.

    Why do you need a main object BusinessEntityAdress that holds a single Adress Entity and a BusinessEntity that holds a List of Adresses? You want to use inheritance, so I created a model that looks similiar to yours. Enums will be supported ( I didnt get it to work with EF 4.2), thats why the Type Enum is a notMapped attribute that encapsulates the enumeration.

    The following console app shows the usage of the model. It creates an Adresses and a People table in database.

    HTH

    Holger

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication1
    {
        using System.ComponentModel.DataAnnotations;
    
        using System.Data.Entity;
    
        public enum AddressType
        {
            Private = 1,
            Work = 2
        }
    
        public class Address
        {
            public int Id { get; set; }
            public string AddressLine1 { get; set; }
            public int Type { get; set; }
    
            [NotMapped]
            public AddressType AddressType
            {
                get
                {
                    return (AddressType)Type;
                }
                set
                {
                    Type = (int)value;
                }
            }
    
        }
    
        public class BusinessEntity
        {
            public BusinessEntity()
            {
                Addresses = new List<Address>();
            }
    
            public int Id { get; set; }
            public DateTime EffectiveDate { get; set; }
            public virtual ICollection<Address> Addresses { get; set; }
        }
    
        public class Person : BusinessEntity
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }
    
    
        public class UserContext : DbContext
        {
            public DbSet<Person> Persons { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Person>().HasMany(p => p.Addresses);
                base.OnModelCreating(modelBuilder);
            }
        }
    
        public class UserContextInitalizer : DropCreateDatabaseAlways<UserContext>
        {
            protected override void Seed(UserContext context)
            {
                var p1 = new Person() { FirstName = "John", LastName = "Doe", EffectiveDate = DateTime.Now };
    
                var adr1 = new Address() { AddressLine1 = "Wallstret 1", AddressType = AddressType.Work };
                var adr2 = new Address() { AddressLine1 = "Broadway 5", AddressType = AddressType.Private };
    
                p1.Addresses.Add(adr1);
                p1.Addresses.Add(adr2);
    
                context.Persons.Add(p1);
                context.SaveChanges();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Database.SetInitializer(new UserContextInitalizer());
                using (var ctx = new UserContext())
                {
                    var p = ctx.Persons.First();
                    Console.WriteLine("Name :" + p.FirstName + " " + p.LastName);
                    foreach (var adr in p.Addresses)
                        Console.WriteLine(adr.AddressType.ToString() + ": " + adr.AddressLine1);
                    Console.ReadKey();
                }
            }
        }
    }
    
    

    The output shous following text:

     

    Name :John Doe
    Work: Wallstret 1
    Private: Broadway 5

    app.config:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <connectionStrings>
        <add name="UserContext"   providerName="System.Data.SqlClient"

    connectionString="data source=localhost;initial catalog=MyContext;persist security info=True;Trusted_Connection=True;

    MultipleActiveResultSets=True;Integrated Security=SSPI" />
      </connectionStrings>
    </configuration>

    Friday, November 25, 2011 7:40 PM
  • Hi Holger,

    Thank you for your reply. BusinessEntityAdress in my model was there, because Entity Address can change over time, so it stores EffectiveDate along with the reference to address. So moving EffectiveDate to BusinessEntity will not serve the purpose. BusinessEntity has not only Billing and/or Shipping, but actually multiple of each, because they can change over time, but history should be stored. Ideally I would even like to refactor and create another class:

    public class TemporalProperty<T>
    {
        public DateTime Effective { get; set; }
        T Item { get; set; }
    }
    

    and then inherit BusinessEntity from it, because same pattern is used in few other places, where history should be stored.

    Thanks!

     

    Saturday, November 26, 2011 10:30 AM
  • Hi,

    the model was a suggestion to demonstrate how to use code first to show how create classes and mappings (what your question was). You are the only person that knows the details of your model, thats why i have choosen a not that complex one for this example.

    To your generic type.. Entity Framwork doesnt support generic types for entites or in entities. IMO.

    Greets 

    Holger

    Saturday, November 26, 2011 11:48 AM