Search code examples
c#entity-framework-6ef-code-first

Entity Framework with code first not initializing Foreign Key class member


I am using EF 6, with code first and I have a domain model class which has 2 FKs and the related objects as class members. The insertion is working fine but when it comes to get the values from within the database, the FKs objects related to this object are null.

I've tried to set Lazy Initialization to false and to mark the FK class members as virtual. I've tried Eagerly Loading and still no positive result (I don't want to use Eagerly anyway).

public class AffectedLocation
{
    public AffectedLocation()
    {
        Id = Guid.NewGuid();
    }

    [Key]
    [Required]
    public Guid Id { get; set; }

    [Required]
    public string Name { get; set; }
    public string Description { get; set; }

    [Required]
    [ForeignKey("RadiationLevel")]
    public Guid FK_RadiationLevel { get; set; }
    public virtual RadiationLevel RadiationLevel { get; set; }

    [Required]
    [ForeignKey("GeographicalCoordinates")]
    public Guid FK_GeographicalCoordinates { get; set; }
    public virtual GeographicalCoordinates GeographicalCoordinates { get; set; 
}

public class RadiationLevel
{
    public RadiationLevel()
    {
        Id = Guid.NewGuid();
    }

    [Key]
    public Guid Id { get; set; }

    [Required]
    public int Level { get; set; }

    public string Description { get; set; }
}

public class GeographicalCoordinates
{
    public GeographicalCoordinates()
    {
        Id = Guid.NewGuid();
    }

    [Key]
    public Guid Id { get; set; }

    [Required]
    public float Latitude { get; set; }

    [Required]
    public float Longitude { get; set; }

    [Required]
    public float Range { get; set; }
}

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly DbContext Context;

    public Repository(DbContext context)
    {
        Context = context;
    }

    public IEnumerable<TEntity> GetAll()
    {
        return Context.Set<TEntity>().ToList();
    }
}

public class LocationRepository : Repository<AffectedLocation>, ILocationRepository
{
    public LocationRepository(RadioactiveAreaContext context)
        : base(context)
    {

    }
}

public class UnitOfWork : IUnitOfWork
{
    private readonly RadioactiveAreaContext _context;

    public UnitOfWork()
    {
        _context = new RadioactiveAreaContext();
        Location = new LocationRepository(_context);
    }

    public ILocationRepository Location { get; private set; }

    public int Complete()
    {
        return _context.SaveChanges();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}

public class RadioactiveAreaContext : DbContext
{
    public RadioactiveAreaContext()
        : base("name=RadioactiveAreaContext")
    {
        this.Configuration.LazyLoadingEnabled = false;
    }

    public virtual DbSet<AffectedLocation> Locations { get; set; }
    public virtual DbSet<RadiationLevel> RadiationLevels { get; set; }
    public virtual DbSet<GeographicalCoordinates> GeographicalCoordinates { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new LocationConfiguration());
        modelBuilder.Configurations.Add(new RadiationLevelsConfiguration());
        modelBuilder.Configurations.Add(new GeographicalCoordinatesConfiguration());
    }
}


var unitOfWork = new UnitOfWork();

unitOfWork.Location.Add(someLocation);

unitOfWork.Complete();

var locations = unitOfWork.Location.GetAll(); // first location will be correctly loaded from the database, please see the first picture (the values are not null)

//Adding a new location and call again the GetAll(), will give 2 results now, but the second result is a EF dynamic proxy and doesn't have the FK class members loaded...

Please have a look at those pictures, getting the data after the first insertion is fine but after the second insertion, when getting the data, this will happen:

two muppets two muppets


Solution

  • When you save location at first time and try to get all locations it will be correctly loaded in the context because you add it in the same context , second time related entities will be null because it is new context

    This happens because Lazy Loading is not enabled

     this.Configuration.LazyLoadingEnabled = false;
    

    To Get related items you should include them Check This

    1-Add GetAllWithInclude to your base Repository

    public IEnumerable<TEntity> GetAllWithInclude(List<string> includes)
    {
        IQueryable<TEntity> entities = Context.Set<TEntity>();
        foreach (var include in includes)
        {
            entities = entities.Include(include);
        }
        return entities.ToList();
    } 
    

    2- add GetAllWithInclude to ILocationRepository interface

    IEnumerable<AffectedLocation> GetAllWithInclude(List<string> includes);
    

    3-to get your data add sub entities you want to include to includes list

    List<string> includes = new List<string>
    {
        "RadiationLevel",
        "GeographicalCoordinates"
    };
    
    List<AffectedLocation> affectedLocations = uow.Location.GetAllWithInclude(includes).ToList();