Add a missing table - asp.net

I miss a table in my data model
I have an existing DB, and tried:
EF Power Tools like THIS description
tables:
User (Id, ...)
Project (ID, Name,..)
Timestamp (ID,Start, End, UserID, ProjectID..)
But I have no Table "Poject_Favorit" (UserId, ProjectID)
So I write the Table myself:
public class Project_Favorite
{
[Key]public Guid GuidUser { get; set; }
public Guid GuidProject { get; set; }
}
and I added:
public DbSet<Project_Favorite> Project_Favorite { get; set; }
to my DbContext
EF searched for "dbo.Project_Favorite" but it should search for "Timeworx.Project_Favorite"
so I added:
modelBuilder.Configurations.Add(new Project_FavoriteMap());
to my DbContext
and created the file "Project_FavoriteMap.cs:
class Project_FavoriteMap : EntityTypeConfiguration<Project_Favorite>
{
public Project_FavoriteMap() {
//Primary Key
this.HasKey(t => t.GuidProject);
this.HasKey(t => t.GuidUser);
this.ToTable("Project_Favorite", "Timeworx");
}
}
Now there is an error. It says "...Project_Favorite already defined..."

It looks like the Poject_Favorit table is a many-to-many join between User and Project. In that case you don't need a corresponding entity class. Your User class will probably have an ICollection<Project> and your Project class will probably have an ICollection<User>. That represents a many-to-many hierarchy. A project can favorited by many users and a user can favorite many projects. Hence the two collections.

Related

SqlException: On Delete Cascade not working ASP.NET

When I try to delete a user from the ASP.NETUsers table I get SqlException:
SqlException: The DELETE statement conflicted with the REFERENCE
constraint "FK_Applications_AspNetUsers_UserID". The conflict occurred
in database "JobGuide", table "dbo.Applications", column 'UserID'.
This problem is occurring because the User's Id is the Foreign key in another table, but "On delete cascade" is not working. For more details this is my model:
My extended Identity User:
public class AppUser : IdentityUser
{
public string Name { get; set; }
public string City { get; set; }
public string RoleName { get; set; }
public virtual ICollection<Application> Applications { get; set; }
}
Application model (i.e. when a user applies for a job):
public class Application
{
public int ApplicationID { get; set; }
public int JobID { get; set; }
public virtual Job Job { get; set; }
public string UserID { get; set; }
public virtual AppUser User { get; set; }
}
Job model:
public class Job
{
public int JobID { get; set; }
public string Title { get; set; }
public virtual ICollection<Application> Applications { get; set; }
}
So up to here I created two One to Many relationships, AspNetUser one to many with Application and Job one to many with Application.
And this is my Fluent API mapping configuration:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Application>()
.HasKey(i => i.ApplicationID);
builder.Entity<Application>()
.HasOne<AppUser>(sc => sc.User)
.WithMany(s => s.Applications)
.HasForeignKey(sc => sc.UserID)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.HasOne<Job>(sc => sc.Job)
.WithMany(s => s.Applications)
.HasForeignKey(sc => sc.JobID)
.OnDelete(DeleteBehavior.Cascade);
}
Delete method from controller:
var userInfo = await userManager.FindByIdAsync(user.Id);
if (userInfo == null)
{
return NotFound();
}
_ = await userManager.RemoveFromRoleAsync(userInfo, userInfo.RoleName);
_ = await userManager.DeleteAsync(userInfo);
int rowsAffected = await db.SaveChangesAsync();
Any idea why this error is not disappearing, is Fluent API good? or i need to type raw Sql to delete the Application with that User once and then the User? I have looked at almost all similar questions but none of them are working for me.
It seems that the cascade delete is not configured in the application table, try to use SSMS to check it:
Open the SQL Server Object Explorer (or using Server Explorer), find the SQL Server Database, then right click the Applications table -> Script As -> CREATE To -> New Query Window, then check whether the table is configured Cascade delete, check this screenshot:
To solve this issue, after configuration Cascade Delete using Fluent API mapping, please remember to enable migration and update the database:
Add-Migration AddCascadeDelete
Update-Database
Besides, you could also configure the Cascade Delete by executing the following SQL command (via SSMS):
ALTER TABLE [dbo].[Applications]
ADD CONSTRAINT [FK_Applications_AspNetUsers_UserID] FOREIGN KEY ([UserID]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;
Can you try setting it the other way around;
builder.Entity<User>()
.HasMany<Application>(u => u.Applications)
.WillCascadeOnDelete();
or use .WillCascadeOnDelete() on your code.

SQLite-Net Extensions | Foreign Key Reference to same entity

I am facing an issue in using SQLite-Net Extensions to save data in local DB in scenario where the foreign key is referencing the same entity (self-join).
Example – Employee and Manager. Every employee has a manager and a manager is also an employee. I am facing issues in saving data in such cases. It will be really helpful if you can provide some insights. Does this extension support this kind of relationship?
Yes, relationships between objects of the same class are supported, but the foreign keys and inverse properties must be explicitly specified in the relationship property attribute because the discovery system will get it wrong as there are be two relationships with the same type.
This example is extracted from the project readme:
public class TwitterUser {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "LeaderId", "Followers",
CascadeOperations = CascadeOperation.CascadeRead)]
public List<TwitterUser> FollowingUsers { get; set; }
// ReadOnly is required because we're not specifying the followers manually, but want to obtain them from database
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "FollowerId", "FollowingUsers",
CascadeOperations = CascadeOperation.CascadeRead, ReadOnly = true)]
public List<TwitterUser> Followers { get; set; }
}
// Intermediate class, not used directly anywhere in the code, only in ManyToMany attributes and table creation
public class FollowerLeaderRelationshipTable {
public int LeaderId { get; set; }
public int FollowerId { get; set; }
}
As you can see here we have a many-to-many between Twitter users. In your case it will be a one-to-many, so you won't need the intermediate table and you'll need the foreign key (ManagerId for example) in your Person class.

ApplicationUser has a list of ApplicationUser

I have built a new Web Application that uses the template Visual Studio provides and included MVC and Web API. The default authorization mechanism is Identity and the database interaction is done using Entity Framework with Code-first method of creating the database.
I have three requirements:
A user can have a list of Children objects
I do not want to use a "relationship" object
All users already exist on the AspNetUsers table, because they all need to be able to login, so I do not want another table to maintain user data
In theory, multiple parents could have reference to multiple children, but for this example, we will just consider it a one-to-many relationship.
In my application, I need to have an ApplicationUser have a list of ChildUsers as a collection of ApplicationUser such as shown below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string ShirtSize { get; set; }
public ICollection<ApplicationUser> Children { get; set; }
}
I want these users to be accessible as shown above (a collection of ApplicationUser), not a collection of Relationship object that ties them together such as:
public class Relationship
{
public String ParentId { get;set; }
public String ChildId { get;set; }
}
Can a new table be created and exist on the database without having a code-first model for it to know how to create a relationship table?
What are available solutions to this problem?
After some research, and experimentation, I have found bits and pieces of guidance to arrive at a solution that works.
In order for an intermediate table to be created to maintain the relationship, the ApplicationDbContext OnModelCreating function needs to know what it should look like. I have told it to create a new table that is not bound to an object by using the modelBuilder shown in the code below. Unfortunately, I do not have the links to the articles that guided me to this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DefaultConnection", throwIfV1Schema: false )
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<ApplicationUser>()
.HasMany( p => p.ChildUsers )
.WithMany()
.Map( m =>
{
m.MapLeftKey( "Father_Id" );
m.MapRightKey( "Son_Id" );
m.ToTable( "father_son_relation" );
} );
}
}
Additionally, when you need to add Children to the parent ApplicationUser, you will need to do some tweaking as you are about to insert so that it updates the database correctly. I definitely want the UserManager to do the creation of the user for me, but that means that when I go to add the user to my list of Children with the code below, it tries to add it again and throws an exception because it already exists.
var result = await UserManager.CreateAsync( user, model.Password );
var myUserId = User.Identity.GetUserId();
var users = AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.ChildUsers );
var u2 = users.First();
u2.ChildUsers.Add( user );
await AppDbContext.SaveChangesAsync();
After finding this question, I researched the EntityStates and found that adding the following line before calling SaveChanges resolved the exception and it no longer attempts to add it again.
AppDbContext.Entry( user ).State = EntityState.Unchanged;
TADA!!! Now to select them from the database using EF, you can then use the following code:
AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.Children ).First();
Since I am only getting one level of Children this will work ok, after that you risk circular references.
Comments and ideas to improve the code are welcome.

Two foreign keys to same primary table

I have two classes: Customer and Association.
A customer can have an association with many customers. Each association is of a defined type (Family, Friend, etc) i.e Customer A is a friend of Customer B. Customer A is related to Customer C. The type of association is defined by an enum AssociationType.
In order to create this in EF i've defined the following classes
public class Customer
{
public string FirstName {get; set;}
public string LastName {get; set;}
public virtual ICollection<Association> Associations { get; set; }
}
public class Association
{
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public int AssociatedCustomerId { get; set; }
public virtual Customer AssociatedCustomer { get; set; }
public AssociationType AssociationType { get; set; }
}
I've removed the Data Annotations as I was unable to get this to compile. I get the error:
"Model compatibility cannot be checked because the database does not
contain model metadata".
Does anyone have any ideas?
It happens sometimes when an error occurs during database creation. The database schema is created then - except the __MigrationHistory table. When you run your application again EF wants to check against the __MigrationHistory table if the schema is still up-to-date with the model and if that table doesn't exist it throws the exception you are having.
To fix the problem either delete the database manually or set the initializer to DropCreateDatabaseAlways<MyContext> (with Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>()) - only once. After the DB is created set it back to your original initializer.
BTW: For your model you will have to specify explicitly that Customer.Associations is related to Association.Customer, either with data annotations...
[InverseProperty("Customer")]
public virtual ICollection<Association> Associations { get; set; }
...or with Fluent API:
modelBuilder.Entity<Customer>()
.HasMany(c => c.Associations)
.WithRequired(a => a.Customer)
.HasForeignKey(a => a.CustomerId);
Thank you Slauma,
your answer got us going in the right direction.
We added the following configuration to the Association configuration:
HasRequired(x => x.AssociatedCustomer).WithMany().WillCascadeOnDelete(false);

EF Code First - Fluent API (WithRequiredDependent and WithRequiredPrincipal)

I have the following class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public Couple Couple { get; set; }
}
public class Couple
{
public Guid Id { get; set; }
public User Groom { get; set; }
public User Bride { get; set; }
}
Important points:
Bride and Groom properties are required
One-to-one relationship
In the User class, it is Couple required
DbContext in OnModelCreating
modelBuilder.Entity<User>().HasRequired(u => u.Couple).WithRequiredPrincipal();
modelBuilder.Entity<Couple>().HasRequired(u => u.Bride).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Groom).WithRequiredDependent();
But I can not be required!
All fileds are with null in the database!.
How do I get the fields in the database as not null?
If possible using the API Flient.
It should be this :
modelBuilder.Entity<User>().HasRequired(u => u.Couple).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Bride).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Groom).WithRequiredDependent();
How WithRequiredDependent Works : Configures the relationship to be required:required without a navigation property on the other side of the relationship. The entity type being configured will be the dependent and contain a foreign key to the principal. The entity
type that the relationship targets will be the principal in the relationship.
Meaning : Let's consider your first line of code here. It creates a foreign key in the entity being configured (User) making it Dependant and making the other side of the relationship (Couple) Principal
Important : Don't you think the configuration you desire will generate a deadlock? I've not tested the code above, but this configuration seems to be a deadlock to me so i'm not sure if EF would allow you to create it. User must need a Couple, and Couple must need that same user i guess.

Resources