如何将类属性(带导航道具)作为实体属性?复杂的类型不会

时间:2017-02-22 22:05:30

标签: c# asp.net-mvc entity-framework

基本上我有一个像:

这样的实体
public class Person {
 public int PersonId { get; set; }
 public string Name { get; set; }
 public Address Hometown { get; set; }
}

和类似:

public class Address {
 public City City { get; set; }
 public string Province { get; set; }
}

我想要完成的是将垂直连接两个类并拥有一个带行的表:

TB_PERSON:
   PersonId PK
   Name
   City_id FK
   Province

为什么我想要这种方法,在我的实际项目中,我在多个条目上发生了相同类型的数据结构模式,在这种情况下,示例将是地址类。它可能很容易出现在另一个实体中。

难道我几天都找不到怎么办?我能得到的是最复杂的类型,但在这种情况下它们不允许导航属性。我希望访问并使我的行数据类型化,面向对象,以为EF会有所作为。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:3)

ComplexType应该是一个解决方案但不幸的是:

复杂类型不能包含导航属性。 Source

变通方法列表:

使用表格拆分的解决方法

public class Person
{
    public int PersonID { get; set; }
    public string Name { get; set; }
    public virtual Address Address { get; set; }
}

public class Address
{
    public Int32 ID { get; set; }
    public string Province { get; set; }
    public virtual City City { get; set; }

}

public class City
{
    public Int32 CityID { get; set; }
    public string Name { get; set; }
}

public class MappingContext : DbContext
{
    public DbSet<Person> Persons { get; set; }

    public DbSet<Address> Addresses { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Address>()
            .HasKey(t => t.ID)
            .HasOptional(t => t.City)
            .WithMany()
            .Map(t => t.MapKey("CityID"));

        modelBuilder.Entity<Address>()
            .Property(t => t.ID)
            .HasColumnName("PersonID");

        modelBuilder.Entity<Person>()
            .HasKey(t => t.PersonID)
            .HasRequired(t => t.Address)
            .WithRequiredPrincipal();

        modelBuilder.Entity<Person>().ToTable("TB_PERSON");

        modelBuilder.Entity<Address>().ToTable("TB_PERSON");

        modelBuilder.Entity<City>()
            .HasKey(t => t.CityID)
            .ToTable("City");
    }
}

[使用方法]

    using (var db = new MappingContext())
    {
        var person = db.Persons.FirstOrDefault();
        var cityName = person.Address.City.Name;

        var address = db.Addresses.FirstOrDefault();
        var personName = address.Person.Name;
    }

[数据库]

    CREATE TABLE [dbo].[City](
        [CityID] [int] IDENTITY(1,1) NOT NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]

    CREATE TABLE [dbo].[TB_PERSON](
        [PersonId] [int] IDENTITY(1,1) NOT NULL,
        [Name] [varchar](50) NULL,
        [Province] [varchar](50) NULL,
        [CityID] [int] NULL
    ) ON [PRIMARY]

使用表拆分+ TPC继承的解决方法(对于可重用的地址类)

TB_CUSTOMER是另一个包含地址列的表。

public class Person
{
    public int PersonID { get; set; }
    public string Name { get; set; }
    public virtual PersonAddress Address { get; set; }
}

public class Address
{
    public string Province { get; set; }
    public virtual City City { get; set; }

}

public class PersonAddress : Address
{
    public Int32 PersonID { get; set; }
    public virtual Person Person { get; set; }
}
public class CustomerAddress : Address
{
    public Int32 CustomerID { get; set; }
}

public class Customer
{
    public int CustomerID { get; set; }
    public string Name { get; set; }
    public virtual CustomerAddress Address { get; set; }
}

public class City
{
    public Int32 CityID { get; set; }
    public string Name { get; set; }
}

public class MappingContext : DbContext
{
    public DbSet<Person> Persons { get; set; }
    public DbSet<Customer> Customers { get; set; }
    public DbSet<PersonAddress> PersonAddresses { get; set; }
    public DbSet<CustomerAddress> CustomerAddresses { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PersonAddress>()
            .HasKey(t => t.PersonID)
            .HasOptional(t => t.City)
            .WithMany()
            .Map(t => t.MapKey("CityID"));

        modelBuilder.Entity<CustomerAddress>()
            .HasKey(t => t.CustomerID)
            .HasOptional(t => t.City)
            .WithMany()
            .Map(t => t.MapKey("CityID"));

        modelBuilder.Entity<Person>()
            .HasRequired(t => t.Address)
            .WithRequiredPrincipal(t => t.Person);

        modelBuilder.Entity<Customer>()
            .HasRequired(t => t.Address)
            .WithRequiredPrincipal();

        modelBuilder.Entity<Person>().ToTable("TB_PERSON");
        modelBuilder.Entity<PersonAddress>().ToTable("TB_PERSON");

        modelBuilder.Entity<Customer>().ToTable("TB_CUSTOMER");
        modelBuilder.Entity<CustomerAddress>().ToTable("TB_CUSTOMER");

        modelBuilder.Entity<City>()
            .HasKey(t => t.CityID)
            .ToTable("City");
    }
}

使用IAddress进行解决方法

public class Person : IAddress
{
    public int PersonID { get; set; }
    public string Name { get; set; }
    public string Province { get; set; }
    public virtual City City { get; set; }

    [NotMapped]
    public IAddress Address { get { return this; } }
}

public interface IAddress
{
    string Province { get; set; }
    City City { get; set; }

}

public class City
{
    public Int32 CityID { get; set; }
    public string Name { get; set; }
}

public class MappingContext : DbContext
{
    public DbSet<Person> Persons { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

        modelBuilder.Entity<Person>()
            .HasKey(t => t.PersonID)
            .HasOptional(t => t.City)
            .WithMany()
            .Map(t => t.MapKey("CityID"));

        modelBuilder.Entity<Person>().ToTable("TB_PERSON");

        modelBuilder.Entity<City>()
            .HasKey(t => t.CityID)
            .ToTable("City");
    }
}

答案 1 :(得分:1)

除了Table分割之外,还有2种解决方法(不是解决方案)。

继承

创建Address类并在每个应该有地址的类中继承它 地址属性与其他属性混合在一起(实际上我认为在您的情况下我不会应用此解决方案)。

1-1关系
(如果更多实体可以共享相同的地址,则为n-1关系)

型号:

public class ClassA
{
    public int Id { get; set; }
    public string Description { get; set; }
    public virtual ClassB ClassB { get; set; }
}

public class ClassB
{
    public int Id { get; set; }
    public string Description { get; set; }
    public virtual ClassA ClassA { get; set; }
}

背景信息:

class Context : DbContext
{
    public Context(DbConnection connection)
        : base(connection, false)
    { }

    public DbSet<ClassA> As { get; set; }
    public DbSet<ClassB> Bs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ClassB>().HasOptional(c => c.ClassA).WithOptionalDependent(c => c.ClassB);
    }
}

DDL声明:

ExecuteNonQuery==========
CREATE TABLE [ClassAs] (
 [Id] int not null identity(1,1)
, [Description] text null
);
ALTER TABLE [ClassAs] ADD CONSTRAINT [PK_ClassAs_9cd06620] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [ClassBs] (
 [Id] int not null identity(1,1)
, [Description] text null
, [ClassA_Id] int null
);
ALTER TABLE [ClassBs] ADD CONSTRAINT [PK_ClassBs_9cd06620] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE INDEX [IX_ClassA_Id] ON [ClassBs] ([ClassA_Id])
ExecuteNonQuery==========
ALTER TABLE [ClassBs] ADD CONSTRAINT [FK_ClassBs_ClassAs_ClassA_Id] FOREIGN KEY ([ClassA_Id]) REFERENCES [ClassAs] ([Id])

在第二种情况下,您可以删除ClassB.ClassA导航属性,以便跨多种类型共享ClassB。这里的问题是你有2个表

答案 2 :(得分:0)

几乎所有复杂的导航都可以使用流畅的api。

Google这些:Fluent Api和EntityTypeConfiguration