EF Core 3.1.4 Navigation property returning null in One-To-One relationship - .net-core

I am having a problem where a navigation property is always returning null in a One-to-One relationship in EF Core 3.1.4.
My models are structured like so:
public class UserCredential
{
public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public byte[] Salt { get; set; }
public bool IsLocked { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public UserProfile UserProfile { get; set; }
}
public class UserProfile
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Suffix { get; set; }
public bool IsDeleted { get; set; }
public List<Address> Addresses {get;set;}
public DateTime DOB { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public Guid UserCredentialId { get; set; }
public UserCredential UserCredential { get; set; }
}
Based off what I understand, that should have been enough for EF Core to infer the One-To-One relationship. But it didnt work so I defined the relationshup in my dbContext like so:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<UserProfile>(entity =>
{
entity.HasOne(up => up.UserCredential)
.WithOne(uc => uc.UserProfile)
.HasForeignKey<UserProfile>( up => up.UserCredentialId);
});
}
I checked the db and there is a foreign key from UserProfile -> UserCredentials defined in the table. Likewise both tables have Id as a primary key.
If I post data to a "addUser" endpoint it will be added correctly in the db.
{
"username": "test3",
"password": "password123",
"UserProfile":{
"FirstName": "John",
"LastName": "Doe"
}
}
Db Screenshot
However "UserProfile" in my model is always null.
System.NullReferenceException: 'Object reference not set to an
instance of an object.'
IronCarp.Identity.Models.UserCredential.UserProfile.get returned null.
I'm using a repository to interact with the db and the method that is returning my data/model seems simple enough.
private async Task<UserCredential> GetUserCredentials(string username)
{
return await context.UserCredentials.Where(u => u.Username == username).FirstOrDefaultAsync();
}
Any help is appreciated, I am not sure what I am missing, thanks!

try to include navigation property in linkq, try something like this:
private async Task<UserCredential> GetUserCredentials(string username)
{
return await context.UserCredentials.Include(x => x.UserProfile).Where(u => u.Username == username).FirstOrDefaultAsync();
}

For me this issue was happening intermittently in my integration tests.
It turned out because I was mistakenly registering the EF Core DbContext as Transient and not Scoped:
Services.AddDbContext<SubscriptionsContext>(
options => options.UseNpgsql(appConfig.DatabaseConnectionString,
optionsBuilder => optionsBuilder.EnableRetryOnFailure(3)), ServiceLifetime.Transient);
Should be:
Services.AddDbContext<SubscriptionsContext>(
options => options.UseNpgsql(appConfig.DatabaseConnectionString,
optionsBuilder => optionsBuilder.EnableRetryOnFailure(3)), ServiceLifetime.Scoped);
If you look at the default parameter on the AddDbContext() extension method, it's always Scoped:

Related

Establishing one to one relations with Entity Framework 7

Having the following parent class:
[Table("smart_recharge_registro")]
public class SmartRechargeRegistro
{
[Key]
public int id { get; set; }
public SmartRechargeRequest request { get; set; }
public SmartRechargeProceso proceso { get; set; }
public SmartRechargeResponse response { get; set; }
}
Which in turn references the following child classes:
[Table("smart_recharge_request")]
public class SmartRechargeRequest
{
public String nombreDeUsuario { get; set; }
public String passwordDeUsuario { get; set; }
public String msisdnSuscriptor { get; set; }
}
and:
[Table("smart_recharge_proceso")]
public class SmartRechargeProceso
{
[Key]
public int id { get; set; }
public String carrierId { get; set; }
public String cliente { get; set; }
public String network { get; set; }
}
and lastly:
[Table("smart_recharge_response")]
public class SmartRechargeResponse
{
public Boolean responseSuccess { get; set; }
public int responseCode { get; set; }
public String? responseDetails { get; set; }
}
The Add-Migration and Update-Database command execute without problems. However, when I try to save
await _repository.RegistroColeccion.AddAsync(registro);
await _repositorio.SaveChangesAsync();
I get the following error:
Microsoft.EntityFrameworkCore.DbUpdateException: Could not save changes. Please configure your entity type accordingly.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Cannot add
or update a child row: a foreign key constraint fails
(beservicebroker_dev.registro_eventos_srdotnet, CONSTRAINT
FK_registro_eventos_srdotnet_SmartRechargeProceso_procesoid FOREIGN
KEY (procesoid) REFERENCES smartrechargeproceso (id) O)
To solve the problem, I tried to create one-to-one relationships following this tutorial
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.request)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeRequest>(r => r.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.proceso)
.WithOne(p => p.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeProceso>(p => p.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.response)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeResponse>(r => r.id);
Inside SmartRechargeRequest, SmartRechargeProceso and SmartRechargeResponse, added the following:
[JsonIgnore]
public SmartRechargeRegistro SmartRechargeRegistro { get; set; }
Also added inside SmartRechargeRequest and SmartRechargeResponse an id
[Key]
public int id { get; set; }
I'm still unable to test the endpoint because the SmartRechargeRequest and SmartRechargeResponse are completely disfigured in the swagger (even if the [JsonIgnore] or [IgnoreDataMember] annotations are set) due to the presence of that SmartRechargeRegistro object.
I'm pretty sure my solution is misguided and I'm getting the process completely wrong.
What would be the proper way to map one-to-one relationships for this case? Any help will be appreciated.
Please note that in reality, these classes are huge (dozens of properties), so it's not possible to merge all of them on a single table.

EF Core 3.18 get sum and count from related table

I have a web api where I am trying to get sum and count of a related table. Using .net core 3 and EF Core 3.1.8.
This is what I have tried:
_context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings.GroupBy(g => g.Bookid).Select(s => new { SumAllVotes = s.Sum(item => item.Rating) }))
.ToListAsync();
But I just get an error message. (see below).
I find it difficault debugging with EF Core as I dont know where it is going wrong. Have been trying a couple of hours, but whatever I write I get the same error message.
Thought maybe you guys were able to see what was wrong.
What I want
I am trying to get Sum of all Rating inside table Ratings.
Rating contains only 0 or 1. And I am trying to sum ratings on each bookid. I wanted to have it in this class public int SumAllVotes { get; set; }.
Because I list out all Books...and one of the properties will then be SumAllVotes. (And also CountAllVotes, when I have finished this problem).
By the end I will have a SumAllVotes and CountAllVotes and can calculate the percentage of how many have pressed "1".
Error message:
An unhandled exception occurred while processing the request.
InvalidOperationException: Lambda expression used inside Include is
not valid.
Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessInclude(NavigationExpansionExpression
source, Expression expression, bool thenInclude)
What I have tried:
[HttpGet]
public async Task<ActionResult<IEnumerable<Books>>> GetBooks()
{
Guid userid = Guid.Parse(this.User.FindFirst(ClaimTypes.NameIdentifier).Value);
return await _context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings.GroupBy(g => g.Bookid).Select(s => new { SumAllVotes = s.Sum(item => item.Rating) }))
.ToListAsync();
}
Books and Ratings are defined as -
public partial class Books
{
public Books()
{
Bookmarks = new HashSet<Bookmarks>();
Comments = new HashSet<Comments>();
Favourites = new HashSet<Favourites>();
BookCategories = new HashSet<BookCategories>();
Resources = new HashSet<Resources>();
Ratings = new HashSet<Ratings>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public byte Scaleoffun { get; set; }
public byte Scaleoflearning { get; set; }
public int? Goal { get; set; }
public int? Secondgoal { get; set; }
public int? Thirdgoal { get; set; }
public int? Subjectid { get; set; }
public int? Categoryid { get; set; }
public string Language { get; set; }
public string Estimatedtime { get; set; }
public string Image { get; set; }
public int? File { get; set; }
public int? Ownerid { get; set; }
public DateTime Createdon { get; set; }
public DateTime? Lastmodifiedon { get; set; }
public string Active { get; set; }
public string Url { get; set; }
public Guid Userid { get; set; }
public byte? Grade { get; set; }
[NotMapped]
public int SumAllVotes { get; set; }
[NotMapped]
public int CountAllVotes { get; set; }
public virtual Categories Category { get; set; }
public virtual Curriculum GoalNavigation { get; set; }
public virtual Users Owner { get; set; }
public virtual Curriculum SecondgoalNavigation { get; set; }
public virtual Subjects Subject { get; set; }
public virtual Curriculum ThirdgoalNavigation { get; set; }
public virtual ICollection<Bookmarks> Bookmarks { get; set; }
public virtual ICollection<Comments> Comments { get; set; }
public virtual ICollection<Favourites> Favourites { get; set; }
public virtual ICollection<BookCategories> BookCategories { get; set; }
public virtual ICollection<Resources> Resources { get; set; }
public virtual ICollection<Ratings> Ratings { get; set; }
}
public partial class Ratings
{
public int Id { get; set; }
public int? Bookid { get; set; }
public string Type { get; set; }
public Int16? Rating { get; set; }
public Guid Userid { get; set; }
public string Subject { get; set; }
public DateTime Createdon { get; set; }
public DateTime? Modifiedon { get; set; }
public byte? Active { get; set; }
public virtual Books Book { get; set; }
//public virtual Users User { get; set; }
}
These are some other solutions I have tried, but got the same error message:
.Include(r=> r.Ratings.Sum(i=>i.Rating))
and
.Include(r => new { m = r.Ratings.GroupBy(g => g.Bookid) })
You don't need to group child entities by parent's Id. When you Include one-to-many child entities, they are added to their parent's child list, and hence grouped by their parent's identity, based on the relationship between them. All you need to do is tell EF what values you want from that child list.
Change your query to -
_context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings)
.Select(p => new
{
// set ALL the primitive properties from Books entity
Id = p.Id,
Title = p.Title,
// etc ...
// set the computed properties
CountAllVotes = p.Ratings.Count,
SumAllVotes = p.Ratings.Sum(x => x.Rating)
// set the related entities
BookCategories = p.BookCategories,
Resources = p.Resources
})
.ToListAsync();
AutoMapper has a ProjectTo method that generates the required query and does the projection (the Select part) automatically. You can use that to avoid the hassle of setting all those properties manually.
I suggest you don't use Include with Select. Read article how to make queries with Projection (Select). Note, that Rating.Rating is nullable and you need to handle this. Here is a possible code sample:
var view = await _context.Books
.Where(your condition)
.Select(item => new
{
//Todo: fill other props
SumAllVotes = item.Ratings.Sum(rating => (Int16?) rating.Rating),
CountAllVotes = item.Ratings.Count,
})
.ToListAsync()

How to extend Application User to hold a collection of orders?

I'm trying to extend Application User (using Code-First) to hold a collection of orders, but I'm getting errors.
My Order class is
public class Order
{
public Order()
{
OrderDetails = new HashSet<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public string UserId { get; set; }
public bool IsDelivered { get; set; }
public bool IsReturned { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
And I'm trying to extend Application user like this
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public string Profession { get; set; }
public string TaxAuthority { get; set; }
public string TaxNumber { get; set; }
public string Address { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string MobilePhone { get; set; }
public bool NewsLetterSubscribe { get; set; } = false;
public DateTime TimeStamp { get; set; } = DateTime.Now;
public ICollection<Order> Orders { get; set; }
}
And I'm getting the following errors:
QCMS.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
QCMS.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
Can you please help me to solve this problem?
UPDATE:
I'm using two db contexts. The one provided for Individual User Account (when the project is first created) and a second one named "qvModel" that is for all other database classes of my project.
public partial class qvModel : DbContext
{
public qvModel()
: base("name=qvModel")
{
}
//APPSETTINGS
public virtual DbSet<AdminLog> AdminLog { get; set; }
public virtual DbSet<WebLog> WebLog { get; set; }
//LANGUAGES
public virtual DbSet<Language> Languages { get; set; }
.
.
.
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Precision attribute for decimals
Precision.ConfigureModelBuilder(modelBuilder);
modelBuilder.Entity<Language>()
.HasMany(e => e.Brochures)
.WithRequired(e => e.Language)
.WillCascadeOnDelete(true);
.
.
.
modelBuilder.Entity<Order>()
.HasMany(c => c.OrderDetails)
.WithRequired(c => c.Order)
.WillCascadeOnDelete(true);
modelBuilder.Entity<ApplicationUser>()
.HasMany(c => c.Orders)
.WithRequired(c => c.User)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}
I found a solution that is very simple.
The solution is to inherit from IdentityDbContext like this
public class qvModel : IdentityDbContext<ApplicationUser>
{
public qvModel()
: base("name=qvModel")
{
}
I was also missing the following line from OnModelCreating
base.OnModelCreating(modelBuilder);
After these changes, my migration is working and I stopped getting the errors I mentioned.

ASP.NET Core Conflict with Foreign Key

I have got several models:
Course.cs
public class Course
{
public Guid Id { get; set; }
public ICollection<ApplicationUser> Teacher { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public DateTime CreationDate { get; set; }
public bool IsActive { get; set; }
}
Group.cs
public class Group
{
public Guid Id { get; set; }
public ApplicationUser Mentor { get; set;}
public string DisplayName { get; set; }
public string GroupName { get; set; }
public DateTime StartYear { get; set; }
public string InviteCode { get; set; }
public ICollection<ApplicationUser> Students { get; set; }
public ICollection<Course> Courses { get; set; }
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
[Required]
public string Firstname { get; set; }
[Required]
public string Surname { get; set; }
public bool Gender { get; set; }
public DateTime Birthdate { get; set; }
//[Required]
public string InviteCode { get; set; }
public Guid GroupId { get; set; }
[ForeignKey("GroupId")]
public Group CurrentGroup { get; set; }
public ICollection<Group> PastGroups { get; set; }
}
Now when I try to register (using Identity) a user (not even trying to give the user a group) I receive this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY
constraint "FK_AspNetUsers_Groups_GroupId". The conflict occurred in
database "aspnet-Project_Dojo-3af15f80-8c62-40a6-9850-ee7a296d0726",
table "dbo.Groups", column 'Id'. The statement has been terminated.
In my modelBuilder I have added some logics for the relations between Group, ApplicationUser (Students) and the Foreign Key:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);\\
builder.Entity<ApplicationUser>()
.HasOne(p => p.CurrentGroup)
.WithMany(b => b.Students)
.HasForeignKey(p => p.GroupId);
}
I don't know what this is exactly doing, but I've been browsing some Stackoverflow threads to come to this code (migrations weren't working without it).
I look forward to a solution for my problem. Once again, I'm not doing ANYTHING with the groups yet when registering.
Thanks in advance!
not even trying to give the user a group
Well there's your problem, it's required.
Either provide a group, or make it optional by making the foreign key nullable (Guid? GroupId).
Because it's currently a non-nullable struct, it'll have a default value of all zeroes (Guid.Empty). This FK is not known in your database, resulting in the error you see.

EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType

I am getting these errors when trying to create a merchant.
FlavorPing.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
FlavorPing.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
UserLogins: EntityType: EntitySet 'UserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
UserRoles: EntityType: EntitySet 'UserRoles' is based on type 'IdentityUserRole' that has no keys defined."
Here is my merchant model:
namespace FlavorPing.Models
{
public class Merchant
{
//Meant to inherit identity.
//[ForeignKey("ApplicationUserId")]
public string ApplicationUserId { get; set; }
[ForeignKey("ApplicationUser")]
public virtual List<ApplicationUser> ApplicationUser { get; set; }
[Key]
public int MerchantID { get; set; }
[Required]
[Display(Name = "Business Name")]
public string MerchantName { get; set; }
[Required]
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string email { get; set; }
//need to create formatting here.
[Required]
[Display(Name = "Web Site Link")]
public string website { get; set; }
//public int MenuItemID { get; set; }
public virtual List<MenuItem> MenuItems { get; set; }
public virtual MerchantDetails MerchantDetails { get; set; }
public ICollection<FollowerMenuItemMerchant> FollowerMenuItemMerchants { get; set; }
}
}
Here is the create controller for merchant, which is where I am getting the error:
// POST: Merchants/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MerchantID,MerchantName,email,website")] Merchant merchant)
{
if (ModelState.IsValid)
{
merchant.ApplicationUserId = User.Identity.GetUserId();
db.Merchants.Add(merchant);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(merchant);
}
Here is my DBContext:
namespace FlavorPing.Models
{
public class FlavorPingContext : IdentityDbContext
{
public FlavorPingContext()
: base("name=FlavorPingContext")
{
}
public System.Data.Entity.DbSet<FlavorPing.Models.Merchant> Merchants { get; set; }
public System.Data.Entity.DbSet<FlavorPing.Models.MenuItem> MenuItems { get; set; }
public System.Data.Entity.DbSet<FlavorPing.Models.MerchantDetails> MerchantDetails { get; set; }
public System.Data.Entity.DbSet<FlavorPing.Models.Follower> Followers { get; set; }
public System.Data.Entity.DbSet<FlavorPing.Models.FollowerMenuItemMerchant> FollowerMenuItemMerchants { get; set; }
public DbSet<IdentityUserLogin> UserLogins { get; set; }
public DbSet<IdentityUserClaim> UserClaims { get; set; }
public DbSet<IdentityUserRole> UserRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder builder)
{
// Primary keys
builder.Entity<Follower>().HasKey(q => q.FollowerID);
builder.Entity<MenuItem>().HasKey(q => q.MenuItemID);
builder.Entity<Merchant>().HasKey(q => q.MerchantID);
builder.Entity<FollowerMenuItemMerchant>().HasKey(q =>
new
{
q.FollowerID,
q.MenuItemID,
q.MerchantID
});
// Relationships
builder.Entity<FollowerMenuItemMerchant>()
.HasRequired(t => t.Follower)
.WithMany(t => t.FollowerMenuItemMerchants)
.HasForeignKey(t => t.FollowerID);
builder.Entity<FollowerMenuItemMerchant>()
.HasRequired(t => t.MenuItem)
.WithMany(t => t.FollowerMenuItemMerchants)
.HasForeignKey(t => t.MenuItemID);
builder.Entity<FollowerMenuItemMerchant>()
.HasRequired(t => t.Merchant)
.WithMany(t => t.FollowerMenuItemMerchants)
.HasForeignKey(t => t.MerchantID);
builder.Conventions.Remove<PluralizingTableNameConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
}
I am trying to follow the example (option2) in this link: EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType
I am trying Option 2 because I want to avoid having two DB's. But I am new to managing a DB so if you think I should do Option 3 please advise as to why, or if you see why I am getting this error please tell me why. Thanks in advance!
Ok I fixed my issue by adding this into my DBContext class.
builder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
builder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
builder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
I think you get the errors because your foreign key attributes aren't in the correct spot (and have the wrong name), instead of this:
public string ApplicationUserId { get; set; }
[ForeignKey("ApplicationUser")]
public virtual List<ApplicationUser> ApplicationUser { get; set; }
You need to do this:
[ForeignKey("ApplicationUser")]
public string ApplicationUserId { get; set; }
public virtual List<ApplicationUser> ApplicationUser { get; set; }
The ID is the foreign key to the virtual entity, not the other way around.

Resources