Below is the stripped down version of my original webproject. I have following classes for the Data access project
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; }
}
Customer have Site entity as one to many relation ship. The service classes are registered using windsor IoC container. The issue is if I use IoC resolved DBContext, the navigation property also loading by default. if we load Customer, Customer.Sites have list values, but expected value is null as I am not enabled lazyloading or include in query.
I am using IoC container lifestyle configuration as PerWebrequest (For testing I am using singleton). But if we use Transient configuration Customer.sites is 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);
}
}
Dependencies used : Castle.Windsor version="3.3.0" EntityFramework version="6.1.3"
Please point me how to make the IoC resolved context to disable eager loading or there is any workaround. Project in github
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<>
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.