C# 代码优先复杂类型还是外键关系?

C# 代码优先复杂类型还是外键关系?,c#,entity-framework,C#,Entity Framework,我正在用EF6编写一个MVC5应用程序,它允许用户输入工作时间表、工作笔记并通过电子邮件向客户发送更新 我希望我的数据库结构由3个表组成(计时器、工作通知和电子邮件)。我可以有3个这样的模型 [Table("Timers")] public partial class TimerModel { public int ID { get; set; } public string User { get; set; } ...etc public virtual Cu

我正在用EF6编写一个MVC5应用程序,它允许用户输入工作时间表、工作笔记并通过电子邮件向客户发送更新

我希望我的数据库结构由3个表组成(计时器、工作通知和电子邮件)。我可以有3个这样的模型

[Table("Timers")]
public partial class TimerModel
{
    public int ID { get; set; }
    public string User { get; set; }
    ...etc

    public virtual CustomerEmailModel CustomerEmail { get; set; }
}

[Table("JobNotes")]
public partial class JobNoteModel
{
    public int ID { get; set; }
    [Column("Username")]
    public string User { get; set; }
    ...etc

    public virtual CustomerEmailModel CustomerEmail { get; set; }
}

[ComplexType]
public partial class CustomerEmailModel
{
    [Display(Name = "Email Customer?")]
    public bool SendEmail { get; set; }

    [DataType(DataType.EmailAddress)]
    public string To { get; set; }

    public string Subject { get; set; }

    [DataType(DataType.MultilineText)]
    public string Body { get; set; }
}
但这显然不会创建电子邮件表,而是将属性添加到计时器和JobNotes表中(例如Email\u sendmail、Email\u to等)

如果我删除[ComplexType]注释,并将我的其他模型更改为具有

    public int? EmailID { get; set; }

    [ForeignKey("EmailID")]
    public virtual CustomerEmailModel CustomerEmail { get; set; }
然后它确实创建了表,但我不确定如何添加新条目(即,电子邮件是动态添加的,具有0..1关系。这意味着我需要显式地添加到电子邮件中,获取添加的条目ID,然后将其分配给另一个模型(计时器或jobnote模型)EmailID

有更好的方法吗

谢谢你的帮助。如果你需要进一步的信息,请告诉我

编辑: 看来,我确实需要提供更多的信息,从我得到的答案到目前为止。 我有两个数据库。一个供应商提供的数据库,用于记录工作、时间表、员工、客户等信息的应用程序。我们称之为DB01。 还有一个数据库(称为DB02)用于我的应用程序(它是一个移动应用程序,用户可以记录时间表或工作通知信息,并将其提交给DB01)

移动应用程序具有具有以下按键输入的用户界面; 开始时间、停止时间、休息时间、作业选择器(下拉列表)、时间表标题、时间表注释、向客户发送电子邮件(复选框)、地址、主题、正文

因此,复杂类型确实可以正常工作(这也是我在interm中使用的)。但是,因为我有另一个页面,也可以向客户发送电子邮件,所以我需要一个单独的电子邮件表(但是,我不希望必须单独保存电子邮件,然后将其分配到时间表或工作通知单)


还有-我需要的唯一表(对于DB02),是存储时间表、作业备忘和电子邮件数据的表。一旦提交了时间表或作业备忘,就可以将其删除或存档。我只需要这些表,因为所有其他相关信息都包含在DB01中。我可以从DB02的视图中检索此数据,并且可以使用存储过程fr将信息从DB02提交到DB01om DB02。(DB02将DB01作为链接服务器)

这是一个将外键与EF6一起使用的简单示例

 public class A
{
    public int Id { get; set; }
    public virtual B Bobject { get; set; }
    public int BId;
    public virtual ICollection<C> Cs { get; set; }
}
 public class B
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<A> As { get; set; }
    }
}
 public class C
    {
        public int Id { get; set; }
        public string TransactionId { get; set; }
        public virtual A Aobj { get; set; }
        public int AId { get; set; }
    }
公共A类
{
公共int Id{get;set;}
公共虚拟B对象{get;set;}
公开招标;
公共虚拟ICollection Cs{get;set;}
}
公共B级
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟ICollection为{get;set;}
}
}
公共C类
{
公共int Id{get;set;}
公共字符串TransactionId{get;set;}
公共虚拟A Aobj{get;set;}
公共int-AId{get;set;}
}

首先,您的数据库有相当大的问题,而且您没有清楚地定义不同的数据集

我已经重新开发了您试图使用Fluent API实现的功能

public class TimeSheet
{
    public TimeSheet()
    {
        this.TimeSheetId = Guid.NewGuid()
                               .ToString();
    }

    public virtual Employee Employee { get; set; }
    public string EmployeeId { get; set; }
    public virtual Job Job { get; set; }
    public string JobId { get; set; }
    public string TimeSheetId { get; set; }
}

public class Employee
{
    public Employee()
    {
        this.EmployeeId = Guid.NewGuid()
                              .ToString();
    }

    public string EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }

    public virtual ICollection<TimeSheet> TimeSheets { get; set; }
    public virtual ICollection<Mail> MailsSent { get; set; }
}

public class Job
{
    public Job()
    {
        this.JobId = Guid.NewGuid()
                         .ToString();
    }

    // One job will have one client
    public virtual Client Client { get; set; }
    public string ClientId { get; set; }
    public string JobId { get; set; }
    public string Name { get; set; }
    public string Notes { get; set; }

    // A Job may have many time sheets
    public virtual ICollection<TimeSheet> TimeSheets { get; set; }
}

public class Client
{
    public Client()
    {
        this.ClientId = Guid.NewGuid()
                            .ToString();
    }

    public string ClientId { get; set; }
    public string EmailAddress { get; set; }

    // A client can have many work packages / jobs.
    public virtual ICollection<Job> WorkPackages { get; set; }
    public virtual ICollection<Mail> Mails { get; set; }
}

public class Mail
{
    public Mail()
    {
        this.MailId = Guid.NewGuid()
                          .ToString();
    }

    // A mail item will reference one client.
    public virtual Client Client { get; set; }
    public string ClientId { get; set; }
    public string MailId { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }

    public string EmployeeId { get; set; }

    // A mail item will also originate from an employee
    public virtual Employee Employee { get; set; }

    // This doesn't belong here... as if it isn't 
    // being sent, then it wouldn't make sense to create 
    // create the email in the first place...
    // If you want to queue emails, rename the field to `IsSent`
    // 
    // public bool SendEmail { get; set; }
}

public class TimeSheetConfiguration : EntityTypeConfiguration<TimeSheet>
{
    public TimeSheetConfiguration()
    {
        this.ToTable("TimeSheets");

        this.HasKey(timeSheet => timeSheet.TimeSheetId);
        this.Property(property => property.TimeSheetId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        this.Property(property => property.JobId)      .IsRequired();
        this.Property(property => property.EmployeeId) .IsRequired();

        this.HasRequired(timeSheet => timeSheet.Job)     .WithMany(job => job.TimeSheets).HasForeignKey(timeSheet => timeSheet.JobId);
        this.HasRequired(timeSheet => timeSheet.Employee).WithMany(emp => emp.TimeSheets).HasForeignKey(timeSheet => timeSheet.EmployeeId);
    }
}

public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
    public EmployeeConfiguration()
    {
        this.ToTable("Employees");

        this.HasKey(emp => emp.EmployeeId);
        this.Property(property => property.EmployeeId)  .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        this.Property(property => property.FirstName)   .IsRequired();
        this.Property(property => property.LastName)    .IsOptional();
        this.Property(property => property.EmailAddress).IsRequired();

        this.HasMany(employee => employee.TimeSheets).WithRequired(time => time.Employee).HasForeignKey(time => time.EmployeeId);
        this.HasMany(employee => employee.MailsSent) .WithRequired(mail => mail.Employee).HasForeignKey(mail => mail.EmployeeId);
    }
}

public class ClientConfiguration : EntityTypeConfiguration<Client>
{
    public ClientConfiguration()
    {
        this.ToTable("Clients");

        this.HasKey(client => client.ClientId);
        this.Property(property => property.ClientId)    .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        this.Property(property => property.EmailAddress).IsRequired();

        this.HasMany(property => property.WorkPackages).WithRequired(job  => job.Client) .HasForeignKey(job  => job.ClientId);
        this.HasMany(property => property.Mails)       .WithRequired(mail => mail.Client).HasForeignKey(mail => mail.ClientId);
    }
}

public class JobConfiguration : EntityTypeConfiguration<Job>
{
    public JobConfiguration()
    {
        this.ToTable("Jobs");

        this.HasKey(job => job.JobId);
        this.Property(property => property.JobId)   .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        this.Property(property => property.Name)    .IsRequired();
        this.Property(property => property.ClientId).IsRequired();
        this.Property(property => property.Notes)   .IsRequired();

        this.HasMany(job => job.TimeSheets).WithRequired(time => time.Job)             .HasForeignKey(time => time.JobId);
        this.HasRequired(job => job.Client).WithMany    (client => client.WorkPackages).HasForeignKey(job => job.ClientId);
    }
}

public class MailConfiguration : EntityTypeConfiguration<Mail>
{
    public MailConfiguration()
    {
        this.ToTable("Mails");

        this.HasKey(mail => mail.MailId);
        this.Property(property => property.MailId)    .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        this.Property(property => property.ClientId)  .IsRequired();
        this.Property(property => property.EmployeeId).IsRequired();
        this.Property(property => property.Subject)   .IsRequired();
        this.Property(property => property.Body)      .IsRequired();

        this.HasRequired(mail => mail.Client)  .WithMany(client => client.Mails)        .HasForeignKey(mail => mail.ClientId);
        this.HasRequired(mail => mail.Employee).WithMany(employee => employee.MailsSent).HasForeignKey(mail => mail.EmployeeId); 
    }
}

public class ExampleContext : DbContext
{
    public DbSet<Mail> Mails { get; set; }
    public DbSet<Job> Jobs { get; set; }
    public DbSet<Client> Clients { get; set; }
    public DbSet<Employee> Employees { get; set; }
    public DbSet<TimeSheet> TimeSheets { get; set; }

    /// <summary>
    /// This method is called when the model for a derived context has been initialized, but
    ///             before the model has been locked down and used to initialize the context.  The default
    ///             implementation of this method does nothing, but it can be overridden in a derived class
    ///             such that the model can be further configured before it is locked down.
    /// </summary>
    /// <remarks>
    /// Typically, this method is called only once when the first instance of a derived context
    ///             is created.  The model for that context is then cached and is for all further instances of
    ///             the context in the app domain.  This caching can be disabled by setting the ModelCaching
    ///             property on the given ModelBuidler, but note that this can seriously degrade performance.
    ///             More control over caching is provided through use of the DbModelBuilder and DbContextFactory
    ///             classes directly.
    /// </remarks>
    /// <param name="modelBuilder">The builder that defines the model for the context being created. </param>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new MailConfiguration());
        modelBuilder.Configurations.Add(new ClientConfiguration());
        modelBuilder.Configurations.Add(new EmployeeConfiguration());
        modelBuilder.Configurations.Add(new TimeSheetConfiguration());
        modelBuilder.Configurations.Add(new JobConfiguration());

        base.OnModelCreating(modelBuilder);
    }
}
公共类时间表
{
公共时间表()
{
this.TimeSheetId=Guid.NewGuid()
.ToString();
}
公共虚拟员工{get;set;}
公共字符串EmployeeId{get;set;}
公共虚拟作业作业{get;set;}
公共字符串JobId{get;set;}
公共字符串时间表{get;set;}
}
公营雇员
{
公职人员()
{
this.EmployeeId=Guid.NewGuid()
.ToString();
}
公共字符串EmployeeId{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串电子邮件地址{get;set;}
公共虚拟ICollection时间表{get;set;}
公共虚拟ICollection MailsSent{get;set;}
}
公开课工作
{
公职()
{
this.JobId=Guid.NewGuid()
.ToString();
}
//一个作业将有一个客户机
公共虚拟客户端{get;set;}
公共字符串ClientId{get;set;}
公共字符串JobId{get;set;}
公共字符串名称{get;set;}
公共字符串注释{get;set;}
//一份工作可能有很多时间表
公共虚拟ICollection时间表{get;set;}
}
公共类客户端
{
公共客户机()
{
this.ClientId=Guid.NewGuid()
.ToString();
}
公共字符串ClientId{get;set;}
公共字符串电子邮件地址{get;set;}
//客户机可以有许多工作包/作业。
公共虚拟ICollection工作包{get;set;}
公共虚拟ICollection邮件{get;set;}
}
公营邮件
{
公共邮件()
{
this.MailId=Guid.NewGuid()
.ToString();
}
//邮件项目将引用一个客户端。
公共虚拟客户端{get;set;}
公共字符串ClientId{get;set;}
公共字符串MailId{get;set;}
公共字符串主题{get;set;}
公共字符串体{get;set;}
公共字符串EmployeeId{get;set;}
//邮件项目也将来自员工
公共虚拟员工{get;set;}
//这不属于这里……好像不是
//如果被发送出去,那么创造就没有意义了
//首先创建电子邮件。。。
//如果要对电子邮件进行排队,请将该字段重命名为“IsSent”`
// 
//公共bool sendmail{get;set;}
}
公共类时间表配置:EntityTypeConfiguration
{
公共时间表配置()
{
本表为可折叠表(“时间表”);
this.HasKey(timeSheet=>timeSheet.TimeSheetId);
this.Property(Property=>Property.TimeSheetId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
这是我的财产