我有两个具有一对多关系的实体类。
public class Call : IEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string Username { get; set; }
public virtual ICollection<Call> Calls { get; set; }
}
而且我在Web层上有一个用于'Call'操作的视图模型。
public class CallVm : IViewModel
{
public string Id { get; set; }
public string UserFullname { get; set; }
}
并且我使用一种方法将我的'Call'对象转换为'CallVm'对象。此方法简要介绍如下。
public CallVm MapCallVm(Call call)
{
return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Fullname };
}
[当我从数据库中读取'Call'实体时,有时包括'User',有时不包括。当我不包括它时,Call对象中没有User属性定义,因为它是延迟加载。因此,我在MapCallVm方法中收到以下错误。
ObjectContext实例已被处置,不能再用于需要连接的操作。
有没有办法检查这个?我只想在有紧急负载时分配UserFullname = call.User?.Fullname
。我能想到的唯一解决方案是使用try-catch进行控制。有其他解决方法吗?
您可以使用DbReferenceEntry.IsLoaded属性。
获取或设置一个值,该值指示是否已加载实体从数据库中。
if (_dbContext.Entry(Call).Reference(e => e.User).IsLoaded)
更新
如果在没有dbContext
的情况下获得价值,则应改为将查询强制为Eager loading
。
阅读以下文章以更好地理解。
Should we disable lazy loading of Entity Framework in web apps?
为@Phong's answer-避免传递DbContext。通常,您的存储库类应将数据库实体映射到简单的POCO / DTO对象。
我建议介绍mapper类。这将帮助您对逻辑进行单元测试
// Interface to inject to repository
public interface ICallMapper
{
CallVm Map(Call call);
}
public class CallMapper : ICallMapper
{
public CallVm Map(Call call)
{
return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Username };
}
}
将映射器传递到存储库,并确保您的对象不再与数据库连接
public class CallRepository : ICallRepository
{
private readonly ICallMapper _callMapper;
public CallRepository(ICallMapper callMapper)
{
_callMapper = callMapper;
}
public IList<CallVm> GetList()
{
// Call DB and get entities
var calls = GetCalls();
// Map DB entities to plain model
return calls.Select(_callMapper.Map).ToList();
}
}
这可让您摆脱错误。并使您的程序更具可构造性和可测试性。