1

下面是我原来的 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; }

}

客户将站点实体作为一对多关系。服务类是使用 Windsor IoC 容器注册的。问题是如果我使用 IoC 解析 DBContext,导航属性也会默认加载。如果我们加载客户,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 version="3.3.0" EntityFramework version="6.1.3"

请指出我如何使 IoC 解析上下文以禁用急切加载或有任何解决方法。 项目在 github

4

1 回答 1

4

I think your main issue is the structure of your Entity Model "Customer" which has the property

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

For lazy loading i think you need to use the 'Virtual' keyword and i usually use ICollection<> instead of IList<>

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

Update 1:

Seeing that you don't want Lazy Loading or proxies:

When using Transient the IOC creates a new instance of your DB context each time you ask it to be resolved which then gives you the results you expect when using your Get's as this is a fresh context with no data that was loaded into it during the Add Customer.

When using Singleton the same DB context instance is used for your Add's and Get's. So when you add the new Customer record with the Sites at the beginning of your test the Context is supplying you with the same set back out with the navigation property "Sites" already populated.

于 2015-12-03T17:26:08.520 回答