EF,我是否真的必须在数据库中创建一个实体才能将其关联起来?

时间:2015-04-27 18:05:16

标签: c# entity-framework

您可以从以下代码中了解事情并未按计划进行:

DateTime now = DateTime.Now;

            fieldBooking.CreatedAt = now;
            fieldBooking.CreatedBy = user;
            fieldBooking.UpdatedAt = now;
            fieldBooking.Field = field;

            fieldBooking.FieldBookingDateRange.CreatedAt = now;
            fieldBooking.FieldBookingDateRange.CreatedBy = user;
            fieldBooking.FieldBookingDateRange.UpdatedAt = now;
            fieldBooking.FieldBookingDateRange.FieldBooking = fieldBooking;

            fieldBooking.FieldBookingDateRange.DateRange.CreatedAt = now;
            fieldBooking.FieldBookingDateRange.DateRange.CreatedBy = user;
            fieldBooking.FieldBookingDateRange.DateRange.UpdatedAt = now;

            FieldBookingMessageThread fieldBookingMessageThread = new FieldBookingMessageThread()
            {
                CreatedAt = now,
                CreatedBy = user,
                UpdatedAt = now,
                SentTo = field.CreatedBy,
                FieldBooking = fieldBooking,
                Subject = user.Profile.FirstName + " has made a booking for your field: " + field.Name,
                FieldBookingMessages = new List<FieldBookingMessage>()
            };

            fieldBookingMessageThread.FieldBookingMessages.Add(
                new FieldBookingMessage(){
                    FieldBookingMessageThread = fieldBookingMessageThread,
                    Body = Request.Params["FieldBookingComment"],
                    CreatedAt = now,
                    CreatedBy = user,
                    UpdatedAt = now,
                    SentTo = field.CreatedBy
                }
            );

            fieldBooking.FieldBookingMessageThread = new List<FieldBookingMessageThread>()
            {
                fieldBookingMessageThread
            };

            this.unitOfWork.FieldBookingRepository.Insert(fieldBooking);

这是一团糟。

  1. 我必须在我的所有实体上设置CreatedAt和CreatedBy等,而不管它们都有...
  2. 我必须将每个对象彼此关联
  3. 应用程序甚至不喜欢它:
  4.   

    无法将值NULL插入列'FieldBookingMessageThreadID',表'** .dbo.FieldBookingMessageThreads';列不允许空值。 INSERT失败。   该语句已终止。

    问题似乎是必须首先创建实体,以便将它们全部链接起来......但这不可能是正确的......必须有一种方法可以使实体框架完成大部分工作工作

    我错过了什么,我哪里出错?

    额外信息:

    现场预订

    public class FieldBooking : ISoftDeletable, ITimeStamps, ICreatedBy
    {
        [Key]
        public int FieldBookingID { get; set; }
    
        [Display( Name = "Created at" )]
        public DateTime CreatedAt { get; set; }
    
        [Display( Name = "Updated at" )]
        public DateTime UpdatedAt { get; set; }
    
        ...
    
        public virtual List<FieldBookingMessageThread> FieldBookingMessageThread { get; set; }
    }
    

    现场预订消息主题

    public class FieldBookingMessageThread : MessageThread, ISoftDeletable, ITimeStamps, ICreatedBy
    {
        [Key]
        public int FieldBookingMessageThreadID { get; set; }
    
        [Display( Name = "Field" )]
        [Required]
        public virtual FieldBooking FieldBooking { get; set; }
    
        ...
    }
    

    FieldMessageThread继承的MessageThread:

    public class MessageThread: ISoftDeletable, ITimeStamps, ICreatedBy
    {
    
        [Display(Name = "Created at")]
        public DateTime CreatedAt { get; set; }
    
        [Display(Name = "Updated at")]
        public DateTime UpdatedAt { get; set; }
    
        [Display(Name = "Deleted at")]
        public DateTime? DeletedAt { get; set; }
    
        [Display(Name = "Created by")]
        public virtual ApplicationUser CreatedBy { get; set; }
    
        [Display(Name = "Sent to")]
        public virtual ApplicationUser SentTo { get; set; }
    
        [Required]
        [MaxLength(350)]
        [Display(Name = "Subject")]
        public string Subject { get; set; }
    }
    

    字段消息类

    public class FieldMessage : Message, ISoftDeletable, ITimeStamps, ICreatedBy
    {
        [Key]
        public int FieldMessageID { get; set; }
    
        [Required]
        public virtual FieldMessageThread FieldMessageThread { get; set; }
    }
    

    插入错误输出:

    INSERT [dbo].[FieldBookingMessageThreads]([CreatedAt], [UpdatedAt], [DeletedAt], [Subject], [CreatedBy_Id], [FieldBooking_FieldBookingID], [SentTo_Id])
    VALUES (@0, @1, NULL, @2, @3, @4, @5)
    SELECT [FieldBookingMessageThreadID]
    FROM [dbo].[FieldBookingMessageThreads]
    WHERE @@ROWCOUNT > 0 AND [FieldBookingMessageThreadID] = scope_identity()
    
    
    -- @0: '28/04/2015 15:20:46' (Type = DateTime2)
    
    -- @1: '28/04/2015 15:20:46' (Type = DateTime2)
    
    -- @2: 'Super has made a booking for your field: Test' (Type = String, Size = 350)
    
    -- @3: 'fb2f22ca-8de9-4206-99ce-d43af3d776da' (Type = String, Size = 128)
    
    -- @4: '6' (Type = Int32)
    
    -- @5: 'fb2f22ca-8de9-4206-99ce-d43af3d776da' (Type = String, Size = 128)
    
    -- Executing at 28/04/2015 15:20:56 +01:00
    
    -- Failed in 76 ms with error: Cannot insert the value NULL into column 'FieldBookingMessageThreadID', table 'FieldLover.dbo.FieldBookingMessageThreads'; column does not allow nulls. INSERT fails.
    The statement has been terminated.
    
    
    
    Closed connection at 28/04/2015 15:20:56 +01:00
    
    A first chance exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll
    'iisexpress.exe' (CLR v4.0.30319: /LM/W3SVC/2063751123/ROOT-5-130747043423504979): Loaded 'C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll'. 
    

2 个答案:

答案 0 :(得分:2)

我使用空白控制台应用程序和EF 6.1.3 nuget包给出了您的问题中指定的信息,尽可能地为您创建了以下MCVE 1:*关系: -

internal class Program
{
    private static void Main(string[] args)
    {
        using (var context = new MyContext())
        {
            var fieldBooking = new FieldBooking();

            var fieldBookingMessageThread = new FieldBookingMessageThread();

            fieldBooking.FieldBookingMessageThread = new List<FieldBookingMessageThread>()
            {
                fieldBookingMessageThread
            };

            context.FieldBookings.Add(fieldBooking);
            context.SaveChanges();
        }
        using (var context = new MyContext())
        {
            foreach (var item in context.FieldBookingMessageThreads.ToList())
            Console.WriteLine(
                "Id: {0}, Created by: {1}, Created at: {2}",
                item.FieldBookingMessageThreadID,
                item.CreatedBy,
                item.CreatedAt);
        }

        Console.ReadKey();
    }
}

public class MyContext : DbContext
{
    private readonly string _user;

    public IDbSet<FieldBooking> FieldBookings { get; set; }
    public IDbSet<FieldBookingMessageThread> FieldBookingMessageThreads { get; set; }

    public MyContext()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
        _user = "Iain Galloway";
    }

    public override int SaveChanges()
    {
        var trackables = ChangeTracker.Entries<ITimestamps>().ToList();

        foreach (var item in trackables.Where(t => t.State == EntityState.Added))
        {
            item.Entity.CreatedAt = DateTime.Now;
            item.Entity.CreatedBy = _user;
            item.Entity.UpdatedAt = DateTime.Now;
            item.Entity.UpdatedBy = _user;
        }
        foreach (var item in trackables.Where(t => t.State == EntityState.Modified))
        {
            item.Entity.UpdatedAt = DateTime.Now;
            item.Entity.UpdatedBy = _user;
        }

        return base.SaveChanges();
    }
}

public interface ITimestamps
{
    DateTime CreatedAt { get; set; }

    string CreatedBy { get; set; }

    DateTime UpdatedAt { get; set; }

    string UpdatedBy { get; set; }
}

public class FieldBooking : ITimestamps
{
    [Key]
    public int FieldBookingID { get; set; }

    [Display(Name = "Created at")]
    public DateTime CreatedAt { get; set; }

    [Display(Name = "Created by")]
    public string CreatedBy { get; set; }

    [Display(Name = "Updated at")]
    public DateTime UpdatedAt { get; set; }

    [Display(Name = "Updated by")]
    public string UpdatedBy { get; set; }

    public virtual List<FieldBookingMessageThread> FieldBookingMessageThread { get; set; }
}

public class FieldBookingMessageThread : ITimestamps
{
    [Key]
    public int FieldBookingMessageThreadID { get; set; }

    [Display(Name = "Created at")]
    public DateTime CreatedAt { get; set; }

    [Display(Name = "Created by")]
    public string CreatedBy { get; set; }

    [Display(Name = "Updated at")]
    public DateTime UpdatedAt { get; set; }

    [Display(Name = "Updated by")]
    public string UpdatedBy { get; set; }

    [Display(Name = "Field")]
    [Required]
    public virtual FieldBooking FieldBooking { get; set; }
}

此程序输出以下内容,符合预期的行为: -

Id: 1, Created by: Iain Galloway, Created at: 28/04/2015 11:14:50

这意味着代码中的其他地方或者可能在您之前创建的数据库中出现问题。您所描述的代码没有任何问题,并且不是“首先必须创建实体,以便将它们全部链接”

您需要将自己的代码删回MCVE。根据我的经验,这样做通常会使错误明显足以发现,或以其他方式为回答者提供足够的信息来帮助解决您的问题。 特别是,我的直觉是问题可能存在于FieldBookingMessageThreadMessageThread之间的继承中。您是否愿意将代码发布到MessageThread课程?

关于第二个问题,有几种方法可以自动化表格上的审核列。我或多或少地使用了这里列出的解决方案: - DateCreated or Modified Column - Entity Framework or using triggers on SQL Server(根据我的MCVE),但是可能值得寻找各种不同的解决方案来找到你喜欢的解决方案。

答案 1 :(得分:1)

为了自动设置CreatedAt / SavedAt,我通过覆盖DbContext类中的SaveChanges解决了这个问题,如下所示:

public partial class MyDbContext : DbContext
{
    public override int SaveChanges()
    {
        IEnumerable<ObjectStateEntry> objectStateEntries;
        ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;

        //Find all Entities that are Added/Modified that inherit from BaseDataItem
        objectStateEntries =
                from item in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
                where (item.IsRelationship == false) &&
                            (item.Entity != null) &&
                            (typeof(BaseDataItem).IsAssignableFrom(item.Entity.GetType()))
                select item;

        DateTime currentTime = DateTime.Now;
        foreach (ObjectStateEntry entry in objectStateEntries)
        {
            BaseDataItem entityBase = entry.Entity as BaseDataItem;

            if (entry.State == EntityState.Added)
                entityBase.CreatedDate = currentTime;

            entityBase.ModifiedDate = currentTime;
        }

        return base.SaveChanges();
    }
}

BaseDataItem是我的所有EntityFramework数据类型的基类,上面有我的审计字段。

这可以通过在DataContext上设置一个属性来扩展为CreatedBy / ModifiedBy,该属性当前正在访问用于以相同方式设置字段的数据(用户属性)。