Entity framework 4 添加新记录时实体框架空引用

Entity framework 4 添加新记录时实体框架空引用,entity-framework-4,ef-code-first,Entity Framework 4,Ef Code First,我目前有一个项目,它使用EntityFramework4.1来登录数据库,这样我们就可以跟踪部署在多个web服务器上的web应用程序。我使用解决方案构建了它 我有这样的代码: logging.Logs.Add(newLog); internal class MyLogging : DbContext { public DbSet<Source> Sources { get; set; } public DbSet<Event> Events { get;

我目前有一个项目,它使用EntityFramework4.1来登录数据库,这样我们就可以跟踪部署在多个web服务器上的web应用程序。我使用解决方案构建了它

我有这样的代码:

logging.Logs.Add(newLog);
internal class MyLogging : DbContext
{
    public DbSet<Source> Sources { get; set; }
    public DbSet<Event> Events { get; set; }
    public DbSet<Log> Logs { get; set; }

    /// <summary>
    /// DO NOT ADD ITEMS TO THIS COLLECTION
    /// </summary>
    public DbSet<LogArchive> LogArchives { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new MyDbContextInitializer());

        modelBuilder.Entity<Event>().Property(p => p.EventId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        modelBuilder.Entity<Source>().Property(p => p.SourceId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        modelBuilder.Entity<LogArchive>().Property(p => p.LogId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        base.OnModelCreating(modelBuilder);
    }

    //public class MyDbContextInitializer : DropCreateDatabaseIfModelChanges<MyLogging>
    public class MyDbContextInitializer : CreateDatabaseIfNotExists<MyLogging>
    {
        protected override void Seed(MyLogging dbContext)
        {
            // seed data

            base.Seed(dbContext);
        }
    }
}
这有时会引发以下错误:

System.NullReferenceException:对象引用未设置为 对象的实例。在 System.Data.Objects.ObjectStateManager.DetectConflicts(IList
1
条目)位于System.Data.Objects.ObjectStateManager.DetectChanges()处
System.Data.Entity.Internal.Linq.InternalSet
1.ActOnSet(操作, EntityState新闻状态、对象实体、字符串方法名)位于 System.Data.Entity.Internal.Linq.InternalSet
1.Add(对象实体)位于
System.Data.Entity.DbSet
1.Add(tenty实体)

大多数情况下,它工作正常,但有时会打嗝。当我有多台服务器使用此代码访问/写入同一数据库时,是否有一种最佳做法需要注意

现在使用的方法是,每个请求都会导致系统向集合中添加几个新的日志对象,然后保存它们的分组,而不是保存每个单独的日志记录。这是我的课程大纲

public class LoggingService : ILoggingService
{
    Logging.Model.MyLogging logging;

    public LoggingService()
    {
        InitializeLog();
    }

    /// <summary>
    /// Save any pending log changes (only necessary if using the Add methods)
    /// </summary>
    public void SaveChanges()
    {
        //ensure that logging is not null
        InitializeLog();

        logging.SaveChanges();
    }

    #region Private Methods
    private void InitializeLog()
    {
        if (logging == null)
            logging = new Model.MyLogging();
    }

    private void Log(Level level, int sourceId, string message, bool save, int? siteId = null, int? epayCustomerId = null, 
        string sessionId = null, int? eventId = null, Exception exception = null)
    {
        if (sourceId == 0)
            throw new ArgumentNullException("sourceId", "The Source Id cannot be null and must be valid.");

        var source = (from s in logging.Sources
                     where s.SourceId == sourceId
                     select s).FirstOrDefault();

        if (source == null)
            throw new ArgumentNullException("sourceId", String.Format("No valid source found with Id [{0}].", sourceId));

        if (eventId.HasValue)
        {
            if (eventId.Value > 0)
            {
                var code = (from e in logging.Events
                            where e.EventId == eventId.Value
                            select e).FirstOrDefault();

                //if event id was passed in but no event exists, create a "blank" event
                if (code == null)
                {
                    Event newCode = new Event()
                    {
                        EventId = eventId.Value, 
                        Description = "Code definition not specified."
                    };

                    InitializeLog();
                    logging.Events.Add(newCode);
                    logging.SaveChanges();
                }
            }
        }

        var newLog = new Log()
        {
            Created = DateTime.Now,
            Message = message,
            Source = source,
            Level = level,
            EventId = eventId,
            SessionId = sessionId,
            SiteId = siteId,
            MachineName = System.Environment.MachineName,
        };

        if (exception != null)
            newLog.Exception = String.Format("{0}{1}{2}{1}", exception.Message, Environment.NewLine, exception.StackTrace);

        //ensure that the logging is not null
        InitializeLog();

        logging.Logs.Add(newLog);

        if (save)
        {
            logging.SaveChanges();
        }
    }

    #endregion

}

第一个问题是,您在应用程序期间保持DbContext的打开状态。最佳实践是仅在需要时实例化它,然后对其进行处理。但是这里更大的问题是在多线程环境中使用
LoggingService
(如果我理解正确的话),但是DbContext不是线程安全的。请参见此内容以及上的注释(搜索单词thread)。因此,您应该以线程安全的方式将日志条目保存在其他地方,而不是DbContext中,并且只有在您想要访问数据库时才打开DbContext


+1而不是
InitializeLog()
方法签出可能是EF中的错误,也可能是代码中的错误,很难说。如果你发布了实体类的定义,也许我们可以重现这个问题,否则很难看到它。您还可以尝试隔离代码,并使用伪随机数据在无限循环中运行日志记录,直到再次发生

不过,如果我可以的话,事情很少。您对日志服务中初始化的上下文有点偏执。如果您在构造函数中初始化它,那么以后就无法不初始化它。既然您已经在使用容器,为什么不将上下文作为构造函数的参数,让容器为您注入它呢。你们班基本上是个控制狂(反模式)

    public class LoggingService : ILoggingService
    {
        Logging.Model.MyLogging logging;

        public LoggingService(MyLogging logging)
        {
            // check for null here
            this.logging = logging; 
            //no further null checks 
        }
        ....
     }
此外,当您向容器注册组件时,请将其生命周期设置为http请求范围

For<ILoggingService>().HttpContextScoped().Use<LoggingService>();

通过这种方式,您可以为每个请求获得一组新的对象,这些对象在最后也得到了正确的处理。

SQL Server?或者另一个数据库和提供者?为什么到处都有这些
初始化日志
呼叫?在构造函数中初始化上下文,这还不够吗?如果您真的要在
SaveChanges
方法中创建一个新上下文,然后立即调用该上下文的
SaveChanges
,则不会发生任何事情,因为上下文为空。如果上下文为
null
,我更愿意让应用程序崩溃,因为很可能出现了问题。如果以静默方式创建新上下文,可能会错过一个重要错误。Sql Server 2008 R2。谢谢你的指点。我会把它清理干净。我不知道我在哪里违反了线程安全。IoC容器不会创建单例,因此它不会在应用程序的所有线程之间共享。DBContext是一个私有实例变量,在类的生命周期中保持打开状态。如中所述,上下文将自动打开和关闭与数据库的连接。我运行了一个查询,每个打开的web服务器只有一个连接,这是需要的。我希望能够添加多条记录并一次保存所有记录,这样我就不能在每次调用时创建和销毁上下文。@Josh Ok I
我错过了非单例部分。但是错误的暂时性——正如你所说:“有时会抛出这个错误”——仍然指向一些一致性问题。你能给我们看一些你在哪里使用LoggingService的示例代码吗?@Josh看这些代码,我现在没有其他的想法。但是您仍然可以尝试将您的
日志
实体添加到
LoggigService
中的私有列表实例中,并且仅在调用
SaveChanges
之前将它们添加到上下文中(当然,在保存之后清除日志“缓冲区”)。
    public class LoggingService : ILoggingService
    {
        Logging.Model.MyLogging logging;

        public LoggingService(MyLogging logging)
        {
            // check for null here
            this.logging = logging; 
            //no further null checks 
        }
        ....
     }
For<ILoggingService>().HttpContextScoped().Use<LoggingService>();
protected void Application_EndRequest()
{
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}