IoC容器解析了DbContext(EF),导致实体

时间:2015-12-01 18:01:18

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

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

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; }

}

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

我正在使用IoC容器生活方式配置作为PerWebrequest(用于测试我使用的是singleton)。但是如果我们使用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版本=&#34; 3.3.0&#34; EntityFramework版本=&#34; 6.1.3&#34;

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

1 个答案:

答案 0 :(得分:4)

我认为您的主要问题是您的实体模型的结构&#34;客户&#34; 它有财产

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

对于延迟加载,我认为你需要使用&#39;虚拟&#39;关键字和我通常使用ICollection&lt;&gt;而不是IList&lt;&gt;

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

更新1:

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

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

使用Singleton时,相同的数据库上下文实例将用于您的Add和Get。因此,当您在测试开始时使用“站点”添加新的“客户”记录时,“上下文”将使用导航属性&#34;站点&#34;为您提供相同的设置。已经人口稠密。