Inconsistenty"对象引用未设置为对象的实例"在AspNetUsers的主键上

时间:2017-09-01 09:59:34

标签: c# entity-framework linq asp.net-identity nullreferenceexception

我们正在获取经典的空引用异常,"对象引用未设置为对象的实例。"没有什么不寻常的,除了它在IdentityUser上发生的直接来自上下文。

触发异常的行是:

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId)

并在u.Id触发异常。在这种情况下你怎么能成为null?这告诉我allUsers中的一个条目是null,但我不明白当Entity Framework从数据库中提取allUsers时会发生什么。这是在只读控制器操作上发生的,因此我们没有尝试在请求的早期创建用户。

此错误在我们的生产实例上间歇性地发生,但在将同一数据库还原到开发环境时不会发生。

更多上下文代码(尽管我已将此代码简化为相关部分):

Context(ApplicationUser类具有自己的上下文,与大多数其他实体共享的上下文分开):

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

服务:

public class UserService 
{
    private readonly ApplicationDbContext _context;

    public IEnumerable<ApplicationUser> GetUsers()
    {
        var contextUsers = _context.Users;
        return contextUsers;
    }
}

域对象

public class ApplicationUser : IdentityUser
{
    // Id is inherited from IdentityUser
}

控制器操作:

public ActionResult ParticipantLog(int participantId)
{
    var allUsers = UserService.GetUsers();
    var log = LogService.GetParticipantLog(participantId);
    var model = ParticipantLogsModelMapper.Create(log, allUsers);
}

映射器:

public static ParticipantLogsViewModel Create(ParticipantLog log, IEnumerable<ApplicationUser> allUsers)
{
    var actionBy = allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId);
    var model = new ParticipantLogViewModel
    {
        ActionBy = actionBy.DisplayName
    };

    return model;
}

2 个答案:

答案 0 :(得分:2)

自:

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId)

如果allUsers不包含任何记录,则会返回default(T),在此示例中将为null

我已经整理了一个最小的控制台应用程序(引用Microsoft.AspNet.Identity.EntityFramework

static void Main(string[] args)
{
    var p = default(ApplicationUser);
}

public class ApplicationUser : IdentityUser
{
    // Id is inherited from IdentityUser
}

p确实是空的。就其本质而言,FirstOrDefault扩展方法将返回IEnumerable中的第一个元素,或<T>的默认值,如果其中没有任何内容列表,这似乎是在这种情况下发生的事情。

  

此错误在我们的生产实例上间歇性地发生,但在将同一数据库还原到开发环境时不会发生。

如果您已经简化了所显示的代码,您是否已经省略了一些错误检查/处理,导致失败尝试从数据库中检索用户详细信息并返回空集?这是你可以看到这里发生的情景的一种方式。

为了帮助缩小范围,在生产中,您可以在allUsers.FirstOrDefault调用上方放置诊断日志记录行,以检查allUsers.Any()以确认allUsers中是否存在项目,然后尝试将其与您拥有的任何其他诊断结合起来。开发环境的问题在于,尽管它们很少像生产一样获得 oomph ,但相比之下,它们通常具有微不足道的负载量。这导致由于在负载下操作而未被拾取而发生的问题=(

答案 1 :(得分:0)

通过分析数据库服务器,我注意到问题行:

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId)

列举了allUsers背后的整个表格。我期待它只是为相关用户检索行。

我认为这是因为allUsers是IdentityUser对象,而不是标准EF域对象,而u.Id是TKey类型而不是标准原语。

我的解决方法是传入一个IEnumerable我传入我的UserService,其中包含一个在UserManager上调用FindById的方法

class UserService
{
    private readonly UserManager<ApplicationUser> _userManager;

    public UserService(ApplicationDbContext context)
    {
        _userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
    }

    public ApplicationUser Find(string userId)
    {
        return _userManager.FindById(userId);
    }
}