Home > Software engineering >  EF Core 6: Data is Null. This method or property cannot be called on Null values
EF Core 6: Data is Null. This method or property cannot be called on Null values

Time:07-25

I am migrating my project from .Net 4.7 to .Net 6, so I have to migrate my Entity Framework too.

I have generate my first migration with dotnet ef migrations add PortedToEFCore. According to Microsoft, when we deal with existing database, we need to remove all the Up() and Down() from the added migration record from dotnet ef migrations add.

So here is my migration record PortedToEFCore:

public partial class PortedToEFCore : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
           
        }
    }

Then I have to modify some relation table name to match my old .Net EF.

This is my model:

public class Account : ObjectMapper
{
    [Key]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    public Guid ID { get; set; } = Guid.NewGuid();

    [Required]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    [StringLength(50)]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn("UserStore", 1, IsUnique = true)]
    public string UserName { get; set; }

    [Required]
    public string Password { get; set; }

    public string Hash { get; set; }

    public ICollection<Role> Roles { get; set; }

    [ForeignKey("AccountProfile")]
    public Guid? AccountProfileId { get; set; }

    public AccountProfile AccountProfile { get; set; }

    [ForeignKey("Store")]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn("UserStore", 2, IsUnique = true)]
    public Guid? StoreId { get; set; }
    public Store Store {get;set;}

    public ICollection<Access> Access { get; set; }
}

public class Access
{
    [Key]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    public Guid ID { get; set; } = Guid.NewGuid();

    public string AccessName { get; set; }

    public ICollection<Account> Accounts { get; set; }
}

public class AccountProfile
{
    [Key]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    public Guid ID { get; set; } = Guid.NewGuid();

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

    public string Description { get; set; }

    public string Phone { get; set; }

    public string Email { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string Province { get; set; }
    public string PostCode { get; set; }
}

public class Role
{
    [Key]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    public Guid ID { get; set; } = Guid.NewGuid();

    [Required]
    [StringLength(50)]
    [Toolbelt.ComponentModel.DataAnnotations.Schema.V5.IndexColumn(IsUnique = true)]
    public string RoleName { get; set; }

    public ICollection<Account> Accounts { get; set; }
}

This code bellow is how I match my relation table name, the default relation table names are AccountRole and AccessAccount so this code is for matching the table name into RoleAccounts and AccountAccesses:

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.BuildIndexesFromAnnotations();
        base.OnModelCreating(mb);

        mb.Entity<Account>()
            .HasMany(left => left.Roles)
            .WithMany(right => right.Accounts)
            .UsingEntity<Dictionary<string, object>>(
            "RoleAccounts",
        j => j
            .HasOne<Role>()
            .WithMany()
            .HasForeignKey("Role_ID"),
        j => j
            .HasOne<Account>()
            .WithMany()
            .HasForeignKey("Account_ID"));

        mb.Entity<Account>()
            .HasMany(left => left.Access)
            .WithMany(right => right.Accounts)
            .UsingEntity<Dictionary<string, object>>(
            "AccountAccesses",
        j => j
            .HasOne<Access>()
            .WithMany()
            .HasForeignKey("Access_ID"),
        j => j
            .HasOne<Account>()
            .WithMany()
            .HasForeignKey("Account_ID"));
    }

It can be compiled well, it runs. Then the problem is when I try to retrieve the record.

enter image description here

Notice here the ADMIN account has NULL AccountProfileId and StoreId. According to my model Account this is valid, but this error comes up everytime I try to access the ADMIN account:

System.Data.SqlTypes.SqlNullValueException: 'Data is Null. This method or property cannot be called on Null values.'

My example code:

var xxx = await db.Accounts.ToListAsync();

This code throws that error.

But if I create and connect a new AccountProfile and assign StoreId to Admin account. It can be retrieved alright.

What is this happening? My Account model specifies that AccountProfileId and StoreId are optional, why is this not working?

CodePudding user response:

I would guess, that non-nullable AccountProfile and Store cause this problem. Try to make them nullable to test my hypothesis.

That said, why are you describing migration in your question? You don't have a problem with it, do you? I have done similar conversion in a very different manner - but if you already have a model that matches the database, it probably doesn't matter. But your question is extremely confusing (to me at least) as to what exactly is the problem

  • Related