Entity Framework Core Join identity AspNetUser table. AspNetUser Id to custom tables/Entities - asp.net

I started a new Blazor application and I am trying to use Entity FrameworkCore. I want to link the Identity AspNetUsers table. I want a 1-many relationship with an UserAddress Table. One AspNetUser has many addresses.
public class UserAddress
{
public string Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string ZipCode { get; set; }
' I don't know what to put here to connect this table with the AspNetUsers Table
public int UserId {get;set} This doesn't work
}
I don't know what to do to have EF construct the 1 to many relation between the AspNetUsers Id and the UserAddress table

You can create a one-to-many relationship like this.
UserAddress class:
public class UserAddress
{
public string Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string ZipCode { get; set; }
}
New ApplicationUser inherits Identityuser:
public class ApplicationUser:IdentityUser
{
public ICollection<UserAddress> UserAddresses { get; set; }
}
Make changes in your ApplicationDbContext as follows:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(b => { b.HasMany(p => p.UserAddresses); }) ;
}
public DbSet<UserAddress> UserAddresses { get; set; }
}
Then start the migration and update the database.
Result:

Related

FK for composite key splitted into 2 when table with certain name is added

So I have the following entities defined.
internal class DeliveryArea
{
public string Postcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public ICollection<DeliveryPrice> HasDeliveryPrices { get; set; }
}
internal class DeliveryPrice
{
public uint Id { get; set; }
public DeliveryArea ForDeliveryArea { get; set; }
public string DeliveryAreaPostcode { get; set; }
public string DeliveryAreaState { get; set; }
public string DeliveryAreaCountry { get; set; }
}
and my DbContext is as follow
internal class MyDbContext : DbContext
{
// DbSets.
public DbSet<DeliveryArea> DeliveryAreas { get; set; }
public DbSet<DeliveryPrice> DeliveryPrices { get; set; }
// Overrides.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(#"Data Source=Test.EFCore.db;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region DeliveryArea.
{
var entity = modelBuilder.Entity<DeliveryArea>();
// Setup case-insensitive columns.
entity.Property(i => i.Postcode).HasColumnType("TEXT COLLATE NOCASE");
entity.Property(i => i.State).HasColumnType("TEXT COLLATE NOCASE");
entity.Property(i => i.Country).HasColumnType("TEXT COLLATE NOCASE");
// Setup composite PK.
entity.HasKey(nameof(DeliveryArea.Postcode), nameof(DeliveryArea.State), nameof(DeliveryArea.Country));
}
#endregion
#region DeliveryPrice.
{
var entity = modelBuilder.Entity<DeliveryPrice>();
// DeliveryPrice x DeliveryArea | many-to-one
entity.HasOne(left => left.ForDeliveryArea)
.WithMany(right => right.HasDeliveryPrices)
.HasForeignKey(left => new { left.DeliveryAreaPostcode, left.DeliveryAreaState, left.DeliveryAreaCountry });
}
#endregion
}
}
When the database is generated, EF Core manage to generate appropriate FK that connects both table using the composite key. Everything looks fine and the diagram looks great.
Now, I added the following entity
internal class Currency
{
public uint Id { get; set; }
public ICollection<DeliveryPrice> ForDeliveryPrices { get; set; }
}
and updated DeliveryPrice class as follow
internal class DeliveryPrice
{
public uint Id { get; set; }
// Add the following
public Currency HasCurrency { get; set; }
public uint HasCurrencyId { get; set; }
public DeliveryArea ForDeliveryArea { get; set; }
public string DeliveryAreaPostcode { get; set; }
public string DeliveryAreaState { get; set; }
public string DeliveryAreaCountry { get; set; }
}
My DbContext is updated on top of existing, as follow
internal class MyDbContext : DbContext
{
// DbSets.
public DbSet<Currency> Currencies { get; set; }
// Existing codes remain...
// Overrides.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region Currency.
{
var entity = modelBuilder.Entity<Currency>();
// Currency x DeliveryPrice | one-to-many
entity.HasMany(left => left.ForDeliveryPrices)
.WithOne(right => right.HasCurrency)
.HasForeignKey(right => right.HasCurrencyId);
}
#endregion
// Existing codes remain...
}
}
When the new database is generated, the FK that connects both DeliveryArea and DeliveryPrice table is kinda splitted into 2, as follow
The funny thing is that when the Currencies table is renamed to, say Foo, the FK that connects both DeliveryArea and DeliveryPrice table looks OK.
UPDATE 01:
Normal looking FK
Here's a screenshot of the generated FK that splitted into 2
UPDATE 02:
Upon looking further into the issue, I've found that this is specific to DBeaver only. Viewing the same database file with other database viewer (e.g. DbSchema) does not have the issue.
Any idea what's going on?

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.

Entity Framework Code First Foreign Key Columnname inheritence

Entity Framework code first (v6) creates a columnname in the database that I don't like. In tablename SharepointMappings it adds columnname: 'SharepointDestination_DestinationId' (foreign key).
It also generates a columnname SharepointDestinationId.
I would like to have 1 column, a foreign key, with the name 'SharepointDestinationId'.
My model looks like this:
public class Destination
{
public int DestinationId { get; set; }
}
public class SharepointDestination : Destination
{
public string UserName { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public string SiteUrl { get; set; }
public string DocumentLibraryName { get; set; }
public List<SharepointMapping> Mappings { get; set; }
}
public class SharepointMapping
{
public int SharepointMappingId { get; set; }
public string SourceFieldName { get; set; }
public string DestinationFieldName { get; set; }
//[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
//[ForeignKey("SharepointDestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
}
//.....
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// To use TPT inheritence
modelBuilder.Entity<SharepointDestination>().ToTable("SharepointDestinations");
//modelBuilder.Entity<SharepointMapping>()
// .HasRequired(m => m.SharepointDestination)
// .WithMany(d => d.Mappings)
// .HasForeignKey(m => m.SharepointDestinationId)
// .WillCascadeOnDelete(true);
}
It doesn't matter if i leave or add the attribute ForeignKey and it also doesn't matter if i make properties virtual or not. Completely deleting both properties on SharepointMapping or giving them a complete other name has no consequences.
I think this has something to do with the inheritence structure. Because it's 'only' a 1-n mapping.
How should I configure EF to have only 1 column with the name 'SharepointDestinationId' which should be a foreign key? (and also have the navigation property and DestinationId property on the SharepointMapping class)
Since the key of SharepointDestination is DestinationId, EF can't automatically figure it out. You could go with the annotation:
[ForeignKey("DestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
and remove this:
[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
The fluent should work as well if you comment out the annotation:
modelBuilder.Entity<SharepointMapping>()
.HasRequired(m => m.SharepointDestination)
.WithMany(d => d.Mappings)
.HasForeignKey(m => m.DestinationId)
.WillCascadeOnDelete(true);
The ForeignKey attribute is expecting a property name, not a table column name.
Really, you should be able to do this without any attributes.
The following should work:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
public int ParentId { get; set; }
}

Many to many relationships in EF5 Code First, how can I specify table name?

I'm quite new to EF, and I'm not really sure how to do this.
I have a many-to-many relationship, exactly like this:
When I try to add a resource (Recurso) to a profile (Perfil), I get the following error:
Invalid object name 'dbo.RecursoPerfils
Where the hell did RecursoPerfils come from?
How can I specify (preferably through attribute annotation) the table name for this relationship?
See the models below:
[Table("Perfil")]
public class Perfil
{
public Perfil()
{
this.Usuarios = new List<Usuario>();
this.Recursos = new List<Recurso>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int IdPerfil { get; set; }
[Required]
public string Descricao { get; set; }
public virtual ICollection<Usuario> Usuarios { get; set; }
public virtual ICollection<Recurso> Recursos { get; set; }
}
[Table("Recurso")]
public class Recurso
{
public Recurso()
{
this.Perfis = new List<Perfil>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int IdRecurso { get; set; }
[Required]
public string NomeRecurso { get; set; }
[Required]
public string Descricao { get; set; }
public virtual ICollection<Perfil> Perfis { get; set; }
}
You need to use Fluent API to configure the table name of the join table.
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Perfil>()
.HasMany(p => p.Recursos)
.WithMany(r => r.Perfis)
.Map(mc =>
{
mc.MapLeftKey("IdPerfil");
mc.MapRightKey("IdRecurso");
mc.ToTable("PerfilRecurso");
});
}
}
You can go through this Fluent API relationship mapping tutorial for more info

Resources