How to relate Abpuser Table to other tables ABP Webframework - abp

I have a requirement. There are 3 types of users
Social User
Influencer
Business User
These three users need to be linked to same AbpUser Table. Something like. One AbpUser has one to one relationship with Social, Influencer and Business user. Right now I am not able to make the Social user having reference to Abpuser. same with other user types.
I have the Following classes,
public class SocialUser : FullAuditedAggregateRoot<Guid>
{
public Guid AppUserId { get; set; }// Foreign key referenceing the Appuser
public string DisplayName { get; set; }
// Other code block removed for clarity
}
And Appuser Table like this,
public class AppUser : FullAuditedAggregateRoot<Guid>, IUser
{
public string UserName { get; set; }
public string Email { get; set; }
// ..... Other code block removed for clarity.
}
builder.Entity<AppUser>(u =>
{
u.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser
u.ConfigureByConvention();
u.ConfigureAbpUser();
u.HasOne<SocialUser>().WithOne().HasForeignKey<SocialUser>(x => x.AppUserId).IsRequired();
});
// Other code blocks removed for clarity
}
But while running the Migrations, the AppSocialUser doesn't have foreign key constraint referring to AppUser.
Any ideas, Please let me know. Your valuable suggestions and input make my life easy. Struggling for the past one week. Thank you.

Try:
Entity:
public class SocialUser : FullAuditedAggregateRoot<Guid>
{
public Guid UserId { get; set; }
public string DisplayName { get; set; }
//...
}
DbContextModelCreatingExtensions:
builder.Entity<SocialUser>(b =>
{
b.ConfigureByConvention();
});
MyProjectNameDbContext:
builder.Entity<SocialUser>(b =>
{
b.HasOne<AppUser>().WithMany().HasForeignKey(x => x.UserId);
});
MyProjectNameMigrationsDbContext:
builder.Entity<SocialUser>(b =>
{
b.HasOne<IdentityUser>().WithMany().HasForeignKey(x => x.UserId);
});

Entity:
public class Influencer : FullAuditedAggregateRoot<Guid>
{
public Guid AppUserId { get; set; }// Foreign key referenceing the Appuser
[ForeignKey(nameof(AppUserId))]
public AppUser AppUser { get; set; }
public string DisplayName { get; set; }
}
MyProjectNameMigrationsDbContext :
builder.Entity<AppUser>(b =>
{
b.ToTable("AbpUsers");
b.ConfigureAbpUser();
b.ConfigureFullAuditedAggregateRoot();
b.HasOne<IdentityUser>().WithOne().HasForeignKey<AppUser>(e => e.Id);
b.ConfigureCustomUserProperties();
});

Related

Adding a Role to an User inserts NULL into foreign key column ApplicationUserId in table AspNetUserRoles

I have a custom derived class ApplicationUser from IdentityUser. The mapping table AspNetUserRoles consists of the following columns:
The problem is, when I add a role to an user, .AddToRoleAsync() method works fine, EF inserts a new row into the table AspNetUserRoles, UserId and RoleId get correct values, but ApplicationUserId gets NULL value.
What am I missing here?
Here is my code:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
}
public int IdUser { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
}
public class ApplicationRole : IdentityRole, IModifiable
{
public ApplicationRole(): this(null)
{
}
public ApplicationRole(string name) : base(name)
{
this.Id = Guid.NewGuid().ToString();
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
There are a few questions already asked, but nothing seems to solve my issue.

How can I solve on delete no action or on update no action problen when using code-first in Entity Framework with authentication

I'm creating a project for hospital automation in user authentication and using code-first in Entity Framework.
Here my Hospital entity:
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
Here my Clinic entity:
public int Id { get; set; }
public string Name { get; set; }
And my HospitalAndClinic entity:
public int Id { get; set; }
public int HospitalId { get; set; }
public int ClinicId { get; set; }
[ForeignKey("HospitalId")]
public Hospital Hospital { get; set; }
[ForeignKey("ClinicId")]
public Clinic Clinic { get; set; }
This is the Doctor entity:
public string Branch { get; set; }
public int? HospitalAndClinicId { get; set; }
[ForeignKey("HospitalAndClinicId")]
public HospitalAndClinic HospitalAndClinic { get; set; }
This is my employee entity
public string Position { get; set; }
public int HospitalAndClinicId { get; set; }
[ForeignKey("HospitalAndClinicId")]
public HospitalAndClinic HospitalAndClinic { get; set; }
My Doctorand 'Employee' tables extend from Person class that has fields like id, name etc.
When I do migration I get this problem
Introducing FOREIGN KEY constraint 'FK_Doctor_HospitalAndClinic_HospitalAndClinicId' on table 'Doctor' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
How can I solve this?
In the migration, under constrains you can add onDelete property to say what happens when deleted.
migrationBuilder.CreateTable(
name: "Doctor",
columns: table => new
{
Id = table.Column<int>(nullable: false),
.
.
},
constraints: table =>
{
table.PrimaryKey("PK_Doctor", x => x.Id);
table.ForeignKey(
name: "FK_Doctor_HospitalAndClinic_HospitalAndClinicId",
column: x => x.HospitalAndClinicId,
principalTable: "HospitalAndClinic",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction); // <---- Add this.
});
Or what you can also do is, as Hopeless pointed out, use fluent API to configure your model by overriding the OnModelCreating method in your derived context.
modelBuilder.Entity<Doctor>()
.HasOne(e => e.HospitalAndClinic)
.WithMany()
.OnDelete(DeleteBehavior.NoAction); // <-- Add this
Visit here to see all DeleteBehaviors and visit here to see all ReferentialActions

ThenInclude with many to many in EF Core

I have the following tables:
public class Parent
{
[Key]
public long Id { get; set; }
[ForeignKey("ParentId")]
public List<Person> Persons { get; set; }
}
public class Person
{
[Key]
public long Id { get; set; }
public long ParentId { get; set; }
[ForeignKey("PersonId")]
public List<Friend> Friends { get; set; }
}
public class Friend
{
public long PersonId { get; set; }
public long FriendId { get; set; }
[ForeignKey("FriendId")]
public Person Friend { get; set; }
}
The Friend table is a many-to-many relationship between two rows of the Person table. It has a PK composed from the PersonId and the FriendId, declared like this:
modelBuilder.Entity<Friend>(entity =>
{
entity.HasKey(e => new { e.PersonId, e.FriendId });
});
modelBuilder.Entity<Friend>()
.HasOne(e => e.Friend)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
I want to get all parents with all persons and all their friends, which would look like this:
var entities = context.Parents
.AsNoTracking()
.Include(c => c.Persons)
.ThenInclude(i => i.Friends)
.ToList();
However this does not get the data from the Friends table.
If I inlcude the friends in a person query without the parent it works:
var entities = context.Persons
.AsNoTracking()
.Include(i => i.Friends)
.ToList();
I am using EF Core version 2.2 .
How can I make the first query to retrieve the firends of a person as well and what is causing this behavior?

Null data returned using a 1:1 model

I have two SQL tables, User and UserType joined with UserType as a foreign key, with their respective models in ASP. To my understanding, this should be a 1:1 relationship (correct me if I'm wrong). One unique user, set as a type of user (being admin, super admin, user etc).
When I try and retrieve a list of users, it returns a null on the property UserType.
I used Google to get this far, but I'm struggling to get this particular issue fixed.
At one point I got an error stating: "Unable to determine the principal end of an association". To get around that, I included a Required annotation (didn't work) and a ForeignKey annotation (didn't work either) in both models, both simultaneously and separately.
This is what I have so far.
[Table("Users", Schema = "dbo")]
public class Users
{
[Key, ForeignKey("UserType")]
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ContactNumber { get; set; }
public UserType UserType { get; set; }
public string IsActive { get; set; }
}
[Table("UserType", Schema = "dbo")]
public class UserType
{
[Key]
public Guid Id { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public string IsActive { get; set; }
public Users Users { get; set; }
}
I'm using the below LINQ method to retrieve the data:
public PagedTables<Users> GetAllUsers(Pagination pagination)
{
using (var db = new DbContext())
{
var user = new PagedTables<Users>()
{
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).ToList(),
Count = db.Users.Count()
};
return user;
}
}
A break point on the users var shows that the property UserType returns null. I would expect the assigned user type to be joined onto the user.
Any help would be appreciated. Thanks
My EF background is database-first but if you are eager loading (i.e. not lazy loading) then are you missing an Include to tell LINQ to go and get the UserType? Something like;
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).Include(U => U.UserType).ToList(),

Can I use LINQ to do a join on an IQueryable from SQL Server?

I have two tables. Questions and UserProfile
public QuestionMap()
{
this.HasKey(t => t.QuestionId);
this.Property(t => t.Title)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.CreatedBy)
.IsRequired();
}
public UserProfileMap()
{
this.HasKey(t => t.UserId);
this.Property(t => t.UserName)
.IsRequired()
.HasMaxLength(56);
}
My repository call looks like this at the moment:
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
Is there a way that I can change the repository call so that it will go to the database, join the Question and UserProfile tables and then bring back a list that has the UserName from the UserProfile table? I realize I may have to create another class (that includes UserName) for the return type and I can do that.
You can't configure property mapping to some field of joined entity. But you can create navigation properties in your Question and UserProfile entites, which will provide joined enitites:
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
public virtual UserProfile CreatedBy { get; set; }
}
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
And add mapping configuration to user mapping:
HasMany(u => u.Questions)
.WithRequired(q => q.CreatedBy)
.Map(m => m.MapKey("UserId"))
.WillCascadeOnDelete(true);
Now you will be able to eager load users when querying questions:
var questions = context.Questions.Include(q => q.CreatedBy);
foreach(var question in questions)
Console.WriteLine(question.CreatedBy.UserName);
You should make the join in your controller not your repository. The repository is supposed to deal with only one entity in your database.
Or you can create a View in your database, and add it as an entity to your entity framework model.
Actually, you CAN join tables using LINQ without navigation properties. Let me demonstrate:
public class Question
{
public int QuestionID { get; set; }
public string Title { get; set; }
public string TextBody { get; set; }
public int CreatedBy { get; set; }
}
public class UserProfile
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
So that's the (very simple) model. Now, I'm not at all familiar with the IQueryable<T> GetAll() method shown in the OP's post, but I'm assuming that this method can be used to return an IQueryable<Question> that would, when executed, get all the Questions from the database. In my example, I'm just going to use MyDbContext instead, but this could be replaced with your other method if you prefer it...
So the LINQ join would be done as follows:
public void SomeMethod()
{
var results = MyDbContext.Questions.Join(MyDbContext.UserProfiles,
q => q.CreatedBy,
u => u.UserID,
(q,u) => new {
q.QuestionID,
q.Title,
q.TextBody,
u.UserName
});
foreach (var result in results)
{
Console.WriteLine("User {0} asked a question titled {1}:",
result.UserName, result.Title);
Console.WriteLine("\t{0}", result.TextBody)
}
}
Now, I do think that navigation properties are MUCH better for such obvious and common relationships. However, there are times when you might not want to add a navigation property for a relationship that is seldom referenced. In those cases, it might just be better to use the Join method.

Resources