c# - IoC 容器解决了 DbContext(EF) 导致实体中子对象的急切加载

标签 c# entity-framework ioc-container windsor-3.0

下面是我的原始 web 项目的精简版。我有以下数据访问项目的类

public class DbContextFactory : IDbContextFactory
{
    private readonly DbContext dbContext;
    public DbContextFactory()
    {
        dbContext = new Db();
    }
    public DbContext GetContext()
    {
        return dbContext;
    }
}


public interface IDbContextFactory
{
    DbContext GetContext();
}
public class Entity
{
    public int Id { get; set; }
}
public class Customer : Entity
{
    public string CustomerName { get; set; }
    public string Telephome { get; set; }
    public string Comment { get; set; }
    public IList<Site> Sites { get; set; }
}

public class Site : Entity
{
    public string SiteNumber { get; set; }
    public string Address { get; set; }
    public string PostCode { get; set; }
    public Customer Customer { get; set; }
    [ForeignKey("Customer")]
    public int Customer_Id { get; set; }

}

客户将站点实体作为一对多关系。服务类使用 windsor IoC 容器注册。 问题是如果我使用 IoC 解析 DBContext,导航属性也会默认加载。 如果我们加载 Customer,Customer.Sites 有列表值,但预期值为空,因为我没有启用延迟加载或包含在查询中。

我正在使用 IoC 容器生活方式配置作为 PerWebrequest(为了测试,我正在使用单例)。但是如果我们使用Transient配置Customer.sites为null

public class EFDataAcessTest
{
    private IWindsorContainer iocContainer;
    IDbContextFactory iocCtxFactory;

    [SetUp]
    public void initializer()
    {
        iocContainer = new WindsorContainer();

        iocContainer.Register(Component.For<IDbContextFactory>().ImplementedBy<DbContextFactory>().LifeStyle.Singleton);
        //iocContainer.Register(Component.For<IDbContextFactory>().ImplementedBy<DbContextFactory>().LifeStyle.Transient);

    }

    [Test]
    public void TestCustomerCreation()
    {
        //Creating customer

        var Customer = new Customer() { CustomerName = "Customer 1", Telephome = "78-676-121212", Sites = new List<Site>() };
        Customer.Sites.Add(new Site() { Address = "Site 1", PostCode = "001", SiteNumber = "ST01" });
        Customer.Sites.Add(new Site() { Address = "Site 2", PostCode = "002", SiteNumber = "ST02" });

        iocCtxFactory = iocContainer.Resolve<IDbContextFactory>();
        var iocDBContext = iocCtxFactory.GetContext();

        //adding customer to database
        var sotredCustomer = iocDBContext.Set<Customer>().Add(Customer);
        iocDBContext.SaveChanges();
        var customerId = sotredCustomer.Id;

        //Test
        var nonIoCContext = new DbContextFactory().GetContext();

        var customerFrom_IOC_Context = iocCtxFactory.GetContext().Set<Customer>().Where(c => c.Id == customerId).SingleOrDefault();

        var customerNon_IOC_Context = nonIoCContext.Set<Customer>().Where(c => c.Id == customerId).SingleOrDefault();

        Assert.IsNull(customerNon_IOC_Context.Sites);

        //Expecting empty but having values if IOC lifestyle is singleton or PerWebRequest :(
        //transient is working as expected
        Assert.IsNull(customerFrom_IOC_Context.Sites);
    }

}

使用的依赖项: CaSTLe.Windsor 版本=“3.3.0” EntityFramework 版本="6.1.3"

请指出如何使 IoC 解析上下文禁用预先加载或有任何解决方法。 Project in github

最佳答案

我认为您的主要问题是实体模型“客户”的结构 具有属性

public IList<Site> Sites { get; set; }

对于延迟加载,我认为你需要使用“Virtual”关键字,我通常使用 ICollection<> 而不是 IList<>

private ICollection<Site> _sites;
public virtual ICollection<Site> Sites 
{
    get{ return _sites ?? (_sites = new HashSet<Site>()); }
    set{ _sites = value; }
}

更新 1:

看到您不想要延迟加载或代理:

当使用 Transient 时,IOC 会在您每次要求解析它时创建一个新的 DB 上下文实例,然后为您提供使用 Get 时预期的结果,因为这是一个没有加载任何数据的新上下文在添加客户期间。

当使用 Singleton 时,相同的数据库上下文实例用于您的添加和获取。因此,当您在测试开始时添加带有站点的新客户记录时,上下文会为您提供相同的设置,并且已经填充了导航属性“站点”。

关于c# - IoC 容器解决了 DbContext(EF) 导致实体中子对象的急切加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34027013/

相关文章:

c# - 自动属性初始化与构造函数初始化哪个优先?

c# - Entity Framework 查询以获取子项

design-patterns - 我应该如何将 IoC DI 与此存储库模式结合使用?

.net - 向 IoC 容器注册类型应该由谁负责?

java - Java中支持接口(interface)解析的Ioc/DI

c# - 使用 C# 使用默认浏览器发送 HTTP Post

c# - 这是捕获非特定异常(例如 System.Exception)的坏习惯吗?为什么?

c# - Windows Phone 8.1 连接到蓝牙智能心率监测器

entity-framework - .NET EntityStoreSchemaFilterEntry 过滤器模式

c# - 我们如何让 DynamicData 与 EFPocoAdapter 一起工作?