简体   繁体   中英

Why EF navigation property return null?

I have two model 1)

public class Indicator
{
    public long ID { get; set; }
    public string Name { get; set; }
    public int MaxPoint { get; set; }
    public string Comment { get; set; }
    public DateTime DateChanged { get; set; }
    public DateTime DateCreated { get; set; }

    public virtual IList<CalculationType> CalculationTypes { get; set; }
    public virtual IList<TestEntity> TestEntitys { get; set; }
    public virtual IndicatorGroup IndicatorGroup { get; set; }
}

2)

public class CalculationType
{
    public long ID { get; set; }
    public string UnitName { get; set; }
    public int Point { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateChanged { get; set; }

    public virtual Indicator Indicator { get; set; }
    public virtual IList<Сalculation> Calculations { get; set; }
}

I executing this code

var indicator = DataContext.Indicators.FirstOrDefault(i => i.ID == indicatorID);
var test = DataContext.CalculationTypes.FirstOrDefault();

first line return null on navigation property CalculationTypes在此处输入图像描述

Second line return empty collection.在此处输入图像描述 Why?

UPDATE snapshot database在此处输入图像描述在此处输入图像描述 project link https://github.com/wkololo4ever/Stankin

added Calculation

    public class Сalculation
{
    public long ID { get; set; }

    public virtual CalculationType CalculationType { get; set; }
    public virtual ApplicationUser Creator { get; set; }
}

1) Is Lazy Loading enabled? If not, you need to explicitly load your navigation properties with the '.Include' syntax.

2) Are you sure EF should be able to detect that relation? Did you use Code First or Database First?

Edit: 3) Are you sure there is data in your database and that the foreign key from Indicator to IndicatorGroup has a value for that specific record? I am saying this because the value "null" is valid if there is simply no data.

PS If you do not see a foreign key on Indicator called "IndicatorGroupId", there might be an "IndicatorId" on the table "IndicatorGroup", in which case - going from the names you provided - your database is misconfigured and you will need to use fluent syntax or data attributes to instruct EF on how to make the foreign keys.

Try this:

DbContext.Configuration.ProxyCreationEnabled = true;    
DbContext.Configuration.LazyLoadingEnabled = true;  

If DbContext.Configuration.ProxyCreationEnabled is set to false, DbContext will not load child objects for some parent object unless Include method is called on parent object. Setting DbContext.Configuration.LazyLoadingEnabled to true or false will have no impact on its behaviours.

If DbContext.Configuration.ProxyCreationEnabled is set to true, child objects will be loaded automatically, and DbContext.Configuration.LazyLoadingEnabled value will control when child objects are loaded.

I think this is problem:

Edit: 3) Are you sure there is data in your database and that the foreign key from Indicator to IndicatorGroup has a value for that specific record? I am saying this because the value "null" is valid if there is simply no data.

PS If you do not see a foreign key on Indicator called "IndicatorGroupId", there might be an "IndicatorId" on the table "IndicatorGroup", in which case - going from the names you provided - your database is misconfigured and you will need to use fluent syntax or data attributes to instruct EF on how to make the foreign keys.

Try to this and make sure foreign key is corrected.

public class CalculationType
{
    public long ID { get; set; }
    public string UnitName { get; set; }
    public int Point { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateChanged { get; set; }
    [ForeignKey("IndicatorID")]
    public string IndicatorId { get; set; } //this is the foreign key, i saw in your database is: Indicator_ID, avoid this, rename it to IndicatorID or IndicatorId

    public virtual Indicator Indicator { get; set; }
    public virtual IList<Сalculation> Calculations { get; set; }
}

Same behavior, but different root cause than selected answer:

Navigation property can also be null if you turned off myContext.Configuration.AutoDetectChangesEnabled

Very obvious, but this got me when I was implementing some performance improvments.

Check this out: Navigation Property With Code First . It mentions about why navigation property is null and the solutions of it.

By default, navigation properties are null, they are not loaded by default. For loading navigation property, we use “include” method of IQuearable and this type of loading is called Eager loading.

Eager loading: It is a process by which a query for one type of entity loads the related entities as a part of query and it is achieved by “include” method of IQueryable.

I experienced this issue, where navigation properites were not loaded, even when the Include statement was present.

The problem was caused by string-comparison differences between SQL Server and EF6 using .NET. I was using a VARCHAR(50) field as the primary key in my customers table and also, as a foreign key field in my audit_issues table. What I did not realize was that my keys in the customers table had two additional white space characters on the end; these characters were not present in my audit_issues table.

However, SQL Server will automatically pad whitespace for string comparisons . This applies for WHERE and JOIN clauses, as well as for checks on FOREIGN KEY constraints. Ie the database was telling me string were equivalent and the constraint passed. Therefore I assumed that they actually were exactly equal. But that was false. DATALENGTH of one field = 10, while the DATALENGTH of the other = 8.

EF6 would correctly compose the SQL query to pull the foreign key related fields and I would see them both in the generated Sql query and in the results. However, EF6 would silently fail when loading the Navigation Properties because .NET does not consider those strings equal. Watch out for whitespace in string-type foreign key fields! .

This is a variant of Keytrap 's answer. Using .NET 6 and EF Core 6 , I created a ContextPartials.cs for any custom configurations that I don't want EF's Scaffold command to overwrite:

Required Package:

Install-Package Microsoft.EntityFrameworkCore.Proxies

Code ( ContextPartials.cs ):

// NOTE:  I am not using the new file-scoped namespace on purpose
namespace DataAccess.Models.MyDatabase
{
    // NOTE:  This is a partial outside of the generated file from Scaffold-DbContext
    public partial class MyDatabaseContext
    {
        // NOTE:  This enables foreign key tables to become accessible
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder.UseLazyLoadingProxies();
    }
}

This article helped me.

In sum :

Install-Package Microsoft.EntityFrameworkCore.Proxies

In Startup.cs

using Microsoft.EntityFrameworkCore;

public void ConfigureServices(IServiceCollection services)
        {
              ...
            services.AddDbContext<MyDbContext>(builder =>
            {
                builder.UseLazyLoadingProxies(); // <-- add this
            }, ServiceLifetime.Singleton);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM