Entity framework 将EF配置为在访问未加载的导航属性时抛出(并且禁用了延迟加载)

Entity framework 将EF配置为在访问未加载的导航属性时抛出(并且禁用了延迟加载),entity-framework,entity-framework-4,lazy-loading,eager-loading,Entity Framework,Entity Framework 4,Lazy Loading,Eager Loading,我们有一些应用程序目前正在使用启用延迟加载的EF模型。当我关闭延迟加载(以避免隐式加载和大多数N+1选择)时,我更希望访问一个本应立即加载(或手动加载()的引用)的对象时抛出一个异常,而不是返回null(因为与null ref相比,针对该对象的特定异常更容易调试) 我目前倾向于修改t4模板来实现这一点(因此,如果reference.IsLoaded==false,则抛出),但我想知道这是否已经是一个解决了的问题,无论是在框中还是通过另一个项目 对可以进行源代码分析和检测此类问题的插件/扩展/等的

我们有一些应用程序目前正在使用启用延迟加载的EF模型。当我关闭延迟加载(以避免隐式加载和大多数N+1选择)时,我更希望访问一个本应立即加载(或手动加载()的引用)的对象时抛出一个异常,而不是返回null(因为与null ref相比,针对该对象的特定异常更容易调试)

我目前倾向于修改t4模板来实现这一点(因此,如果reference.IsLoaded==false,则抛出),但我想知道这是否已经是一个解决了的问题,无论是在框中还是通过另一个项目


对可以进行源代码分析和检测此类问题的插件/扩展/等的任何引用的额外积分:

您不应该修改T4。根据提到的“T4”,我猜您正在使用EDMX。容器的属性窗口具有lazyloadingenabled属性。当您创建新模型时,它被设置为true。您可以将其更改为false。T4模板将看到这一点,并将代码添加到ctor中

此外,如果您使用的是Microsoft的POCO模板,他们会将virtual关键字添加到您的导航属性中。Virtual+lazyloadingenabled是获得延迟加载的必要组合。如果删除virtual关键字,则该属性将永远不会延迟加载,如果启用了lazyloading,则不会延迟加载


julie

出于几个与性能相关的原因,我想做同样的事情(延迟加载),我想避免同步查询,因为它们会阻塞线程,在某些地方,我想避免加载完整的实体,而只是加载代码所需的属性

仅仅禁用延迟加载是不够的,因为一些实体的属性可以合法地为null,我不想混淆“null因为它是null”和“null因为我们决定不加载它”

我还想只在我知道延迟加载有问题的特定代码路径中选择性地抛出延迟加载

下面是我的解决方案

在my DbContext类中,添加以下属性:

class AnimalContext : DbContext
{
   public bool ThrowOnSyncQuery { get; set; }
}
在我的代码启动的某个地方,运行以下命令:

// Optionally don't let EF execute sync queries
DbInterception.Add(new ThrowOnSyncQueryInterceptor());
ThrowOnSyncQueryInterceptor
的代码如下:

public class ThrowOnSyncQueryInterceptor : IDbCommandInterceptor
{
    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }

    private void OptionallyThrowOnSyncQuery<T>(DbCommandInterceptionContext<T> interceptionContext)
    {
        // Short-cut return on async queries.
        if (interceptionContext.IsAsync)
        {
            return;
        }

        // Throw if ThrowOnSyncQuery is enabled
        AnimalContext context = interceptionContext.DbContexts.OfType<AnimalContext>().SingleOrDefault();
        if (context != null && context.ThrowOnSyncQuery)
        {
            throw new InvalidOperationException("Sync query is disallowed in this context.");
        }
    }
}

该项目的创建者jamesmanning通过读取堆栈跟踪来拦截延迟加载的调用

因此,在中,您可以创建DbCommandInterceptor,其功能如下:

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        // unfortunately not a better way to detect whether the load is lazy or explicit via interceptor
        var stackFrames = new StackTrace(true).GetFrames();
        var stackMethods = stackFrames?.Select(x => x.GetMethod()).ToList();

        var dynamicProxyPropertyGetterMethod = stackMethods?
            .FirstOrDefault(x =>
                x.DeclaringType?.FullName.StartsWith("System.Data.Entity.DynamicProxies") == true &&
                x.Name.StartsWith("get_"));

        if (dynamicProxyPropertyGetterMethod != null)
        {
              throw new LazyLoadingDisallowedException();
        }
public override void reader执行(DbCommand命令,DbCommandInterceptionContext interceptionContext)
{
//不幸的是,没有更好的方法通过拦截器检测负载是惰性的还是显式的
var stackFrames=new StackTrace(true).GetFrames();
var stackMethods=stackFrames?.Select(x=>x.GetMethod()).ToList();
var dynamicProxyPropertyGetterMethod=stackMethods?
.FirstOrDefault(x=>
x、 DeclaringType?.FullName.StartsWith(“System.Data.Entity.DynamicProxies”)==true&&
x、 Name.StartsWith(“get_”);
if(dynamicProxyPropertyGetterMethod!=null)
{
抛出新的LazyLoadingDisallowedException();
}
我知道读取堆栈跟踪帧可能会很昂贵,尽管我的猜测是,在发生数据访问的正常情况下,与数据访问本身相比,成本可以忽略不计。但是,您需要自己评估此方法的性能


(作为旁注,您所追求的是NHibernate多年来拥有的众多优秀功能之一)。

问题在于,如果我禁用延迟加载,访问未加载的属性(例如EntityCollection的属性)不要抛出,它们只返回null/empty。这意味着假设它们已经加载的各种消费代码将其视为没有子行(例如)不存在。我更愿意更改模板(或者无论如何,以使其工作)因此,如果访问此类集合或导航属性,如果IsLoaded==false,则在访问时抛出。谢谢!我首先要说的是,修改T4可能是一种可行的方法。我肯定会重新考虑IsLoaded为false时抛出的目标。必须有一种更友好的方法来检查该值并对情况做出响应n、 :)非常好!看起来很棒!我不明白这如何有助于检测延迟加载和引发异常。这不只是检测同步查询吗?延迟加载总是发生在同步中,因此如果您在自己的查询中始终使用异步,此方法将查找所有延迟加载。
    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        // unfortunately not a better way to detect whether the load is lazy or explicit via interceptor
        var stackFrames = new StackTrace(true).GetFrames();
        var stackMethods = stackFrames?.Select(x => x.GetMethod()).ToList();

        var dynamicProxyPropertyGetterMethod = stackMethods?
            .FirstOrDefault(x =>
                x.DeclaringType?.FullName.StartsWith("System.Data.Entity.DynamicProxies") == true &&
                x.Name.StartsWith("get_"));

        if (dynamicProxyPropertyGetterMethod != null)
        {
              throw new LazyLoadingDisallowedException();
        }