C# Null Object design pattern behavior in EntityFramework models implementation

121 Views Asked by At

I'm updating the framework of a project from asp.net core 3.1 to .NET 6 following the documentation on microsoft learn. Everything seems transparent enough, so I decided to refactor the code by enabling the null object design pattern.

I noticed a particular behavior in the entity models, in particular a relationship between my custom table and the users table which inherits from IdentityUser.

These were the tables:

public class AspNetUser : IdentityUser<int>
{
    public AspNetUser()
    {
        UserClaims = new HashSet<IdentityUserClaim<int>>();
        UserLogins = new HashSet<IdentityUserLogin<int>>();
        UserTokens = new HashSet<IdentityUserToken<int>>();
        UserRoles = new HashSet<AspNetUserInRoles>();
        ActivityMandatoryUserDocument = new HashSet<ActivityMandatoryUserDocuments>();
        Subscriber = new();
    }

    public virtual ICollection<IdentityUserClaim<int>> UserClaims { get; }
    public virtual ICollection<IdentityUserLogin<int>> UserLogins { get; }
    public virtual ICollection<IdentityUserToken<int>> UserTokens { get; }
    public virtual ICollection<AspNetUserInRoles> UserRoles { get; set; }

    // my custom tables navigation properties
    public virtual Subscriber Subscriber { get; set; }
    public virtual ICollection<ActivityMandatoryUserDocuments> ActivityMandatoryUserDocument { get; set; }  
}
public class Subscriber : BaseEntity<int>
{
    public Subscriber()
    {
      User = new();
    }

    public string? Name { get; set; }
    public string? Surname { get; set; }
    public string? Email { get; set; }
    public string? CF { get; set; }
    public char Sesso { get; set; }
    public string? Ente { get; set; }
    public int? CndcecLevel { get; set; }
    public DateTime LastRemoteUpdate { get; set; }

    public int FK_User { get; set; }
    public virtual AspNetUser User { get; set; };

    public int? FK_Ordine { get; set; }
    [ForeignKey("FK_Ordine")]
    public virtual Ordini_ODCEC? Ordine { get; set; }

    public GuestType? GuestType { get; set; }
    public string? GuestTypeDescription { get; set; }
}

6 For completeness I also report the BaseEntity class:

public abstract class BaseEntity { }

public abstract class BaseEntity<TKey> : BaseEntity
    where TKey : struct
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public virtual TKey Id { get; set; }
}

This is the mapping between the two navigation properties in my DbContext class:

modelBuilder.Entity<Subscriber>()
  .HasOne(f => f.User)
  .WithOne(d => d.Subscriber)
  .HasForeignKey<Subscriber>(d => d.FK_User);

Then, in the application, when I log in for example, the UserManager and SignInManager objects instantly turned off debugging without going into exception execuiting any method of them (SingIn or SignInAsync for example), not even by inserting a global exception middleware. The only thing i see before the application stops is the query executed through the Output Window which I could see colored red through an extension, as if there was an error.

After a few tries, I solved the problem by changing the navigation properties declaration from new() to null!:

public class AspNetUser : IdentityUser<int>
{
    public AspNetUser()
    {
        // ...
        Subscriber = null!;  // was = new();
    }

    // ...
    public virtual Subscriber Subscriber { get; set; }
}

public class Subscriber : BaseEntity<int>
{
    public Subscriber()
    {
      User = null!; // was = new();
    }

    // ...

    public int FK_User { get; set; }
    public virtual AspNetUser User { get; set; };
}

In this way, Entityframework managers no longer have any problems executing their methods.

I was wondering what null! actually does to this point what is the correct way to declare non-nullable navigation properties in this context.

Thanks!

0

There are 0 best solutions below