I have a many to many relationship between contacts and categories. LINQ has created the SQL junction table CategoryContact
. I have tried several different things to try to assign categories to contacts but haven't had any success. Most of the info online is how to do this with the OnModelCreating
method, but I haven't found anything on how to do this in a seeder class.
Here are a few samples of things I tried...
await _dbContext.Categories.Add(contactId).ToString();
category.Add(contact);
foreach (int categoryId in CategoryList)
{
await _addressBookService.AddContactToCategoryAsync(categoryId, contact.Id);
}
All of the above didn't work for reasons I was unable to successfully debug.
Basically I want to assign one of the categories to each contact so the contacts are seeded with a category attached/assigned.
Any help would be greatly appreciated.
Here is the code.
Seeder class
using ContactPro.Data;
using ContactPro.Models;
using Microsoft.AspNetCore.Identity;
namespace ContactPro.Helpers
{
public class DataSeeder
{
private readonly ApplicationDbContext _dbContext;
private readonly UserManager<AppUser> _userManager;
private string? appUserId;
List<Contact> contact = new List<Contact>();
List<Category> category = new List<Category>();
public DataSeeder(ApplicationDbContext dbContext, UserManager<AppUser> userManager)
{
_dbContext = dbContext;
_userManager = userManager;
}
public async Task SeedDataAsync()
{
await SeedUsersAsync();
await SeedContactsAsync();
await SeedCategoriesAsync();
}
private async Task SeedUsersAsync()
{
if (!_dbContext.Users.Any())
{
var demoUser = new AppUser()
{
UserName = "demouser@mail.com",
Email = "demouser@mail.com",
FirstName = "Demo",
LastName = "User",
EmailConfirmed = true
};
await _userManager.CreateAsync(demoUser, "xxxxxx");
}
await _dbContext.SaveChangesAsync();
appUserId = (await _userManager.FindByEmailAsync("demouser@mail.com")).Id;
}
private async Task SeedContactsAsync()
{
try
{
if (!_dbContext.Contacts.Any())
{
contact = new List<Contact>()
{
new Contact()
{
FirstName = "John",
LastName = "Dickens",
Address1 = "12 Main St.",
City = "Concord",
States = ContactPro.Enums.States.NH,
ZipCode = 03101,
Email = "john@mail.com",
BirthDate = new DateTime(1994,8,20),
PhoneNumber = "(212)345-8587",
DateCreated = new DateTime(2023,5,20),
AppUserId = appUserId,
}
};
}
await _dbContext.Contacts.AddRangeAsync(contact);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
Console.WriteLine("************* ERROR *************");
Console.WriteLine("Error Seeding Contacts.");
Console.WriteLine(ex.Message);
Console.WriteLine("***********************************");
throw;
}
}
private async Task SeedCategoriesAsync()
{
if (!_dbContext.Categories.Any())
{
category = new List<Category>()
{
new Category()
{
AppUserId = appUserId,
Name = "_UnCategorized",
},
new Category()
{
Name = "Friend",
AppUserId = appUserId
},
new Category()
{
Name = "Colleague",
AppUserId = appUserId
},
new Category()
{
Name = "Vendor",
AppUserId = appUserId
},
new Category()
{
Name = "Contractor",
AppUserId = appUserId
},
new Category()
{
Name = "Send Invitation",
AppUserId = appUserId
}
};
}
await _dbContext.Categories.AddRangeAsync(category);
await _dbContext.SaveChangesAsync();
}
}
}
Contact model (only included relevant properties)
public class Contact
{
public int Id { get; set; }
[Required]
public string? AppUserId { get; set; }
// virtual
public virtual AppUser? AppUser { get; set; }
public ICollection<Category> Categories { get; set; } = new HashSet<Category>();
}
Category model
public class Category
{
public int Id { get; set; }
[Required]
public string? AppUserId { get; set; }
[Required]
[Display(Name = "Category Name")]
public string? Name { get; set; }
public virtual AppUser? AppUser { get; set; }
public virtual ICollection<Contact> Contacts { get; set; } = new HashSet<Contact>();
}
Update2:
If you have multiple Contact
, you need to specify Category
separately. Please refer to the code below:
public class DataSeeder
{
public static async Task InitializeAsync(IServiceProvider serviceProvider)
{
var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>());
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
string? appUserId;
List<Contact> contact = new List<Contact>();
List<Category> category = new List<Category>();
using (context)
{
if (!context.Users.Any())
{
var demoUser = new AppUser()
{
UserName = "demouser@mail.com",
Email = "demouser@mail.com",
FirstName = "Demo",
LastName = "User",
EmailConfirmed = true
};
await userManager.CreateAsync(demoUser, "Aa12345!");
}
await context.SaveChangesAsync();
appUserId = (await userManager.FindByEmailAsync("demouser@mail.com")).Id;
try
{
if (!context.Contacts.Any())
{
contact = new List<Contact>()
{
new Contact()
{
FirstName = "John",
LastName = "Dickens",
City = "Concord",
Email = "john@mail.com",
BirthDate = new DateTime(1994,8,20),
PhoneNumber = "(212)345-8587",
DateCreated = new DateTime(2023,5,20),
AppUserId = appUserId,
},
new Contact()
{
FirstName = "Tom",
LastName = "Smith",
City = "Concord",
Email = "tom@mail.com",
BirthDate = new DateTime(1994,8,20),
PhoneNumber = "(212)345-8587",
DateCreated = new DateTime(2023,5,20),
AppUserId = appUserId,
}
};
await context.Contacts.AddRangeAsync(contact);
await context.SaveChangesAsync();
}
}
catch (Exception ex)
{
Console.WriteLine("************* ERROR ************");
Console.WriteLine("Error Seeding Contacts.");
Console.WriteLine(ex.Message);
Console.WriteLine("***********************************");
throw;
}
if (!context.Categories.Any())
{
category = new List<Category>()
{
new Category()
{
AppUserId = appUserId,
Name = "_UnCategorized"
},
new Category()
{
Name = "Friend",
AppUserId = appUserId
},
new Category()
{
Name = "Colleague",
AppUserId = appUserId
},
new Category()
{
Name = "Vendor",
AppUserId = appUserId
},
new Category()
{
Name = "Contractor",
AppUserId = appUserId
},
new Category()
{
Name = "Send Invitation",
AppUserId = appUserId
}
};
}
contact[0].Categories = new List<Category>();
contact[0].Categories.Add(category.Find(c => c.Name == "Contractor"));
contact[1].Categories = new List<Category>();
contact[1].Categories.Add(category.Find(c => c.Name == "Vendor"));
context.Contacts.Update(contact[0]);
context.Contacts.Update(contact[1]);
await context.Categories.AddRangeAsync(category);
await context.SaveChangesAsync();
}
}
}
Test Result:
Update:
You mean that the CategoryContact
table is generated after migrating to the database, which contains ContactId
and CategoryId
as the primary key, but it is not filled with data, right?
This is because you didn't populate the Categories
property in Contact
and the Contacts
property in Category
.
Take my code as an example,I added a new Contact()
to operate on the Categories
property and update the database at the end:
public class DataSeeder
{
public static async Task InitializeAsync(IServiceProvider serviceProvider)
{
var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>());
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
string? appUserId;
//Add this line
var contactItem = new Contact();
List<Contact> contact = new List<Contact>();
List<Category> category = new List<Category>();
using (context)
{
if (!context.Users.Any())
{
var demoUser = new AppUser()
{
UserName = "demouser@mail.com",
Email = "demouser@mail.com",
FirstName = "Demo",
LastName = "User",
EmailConfirmed = true
};
await userManager.CreateAsync(demoUser, "Aa12345!");
}
await context.SaveChangesAsync();
appUserId = (await userManager.FindByEmailAsync("demouser@mail.com")).Id;
try
{
if (!context.Contacts.Any())
{
contactItem = new Contact()
{
FirstName = "John",
LastName = "Dickens",
City = "Concord",
Email = "john@mail.com",
BirthDate = new DateTime(1994, 8, 20),
PhoneNumber = "(212)345-8587",
DateCreated = new DateTime(2023, 5, 20),
AppUserId = appUserId,
};
}
}
catch (Exception ex)
{
Console.WriteLine("************* ERROR *************");
Console.WriteLine("Error Seeding Contacts.");
Console.WriteLine(ex.Message);
Console.WriteLine("***********************************");
throw;
}
if (!context.Categories.Any())
{
category = new List<Category>()
{
new Category()
{
AppUserId = appUserId,
Name = "_UnCategorized",
Contacts = contact
},
new Category()
{
Name = "Friend",
AppUserId = appUserId,
Contacts = contact
},
new Category()
{
Name = "Colleague",
AppUserId = appUserId,
Contacts = contact
},
new Category()
{
Name = "Vendor",
AppUserId = appUserId,
Contacts = contact
},
new Category()
{
Name = "Contractor",
AppUserId = appUserId,
Contacts = contact
},
new Category()
{
Name = "Send Invitation",
AppUserId = appUserId,
Contacts = contact
}
};
}
//Operate the database at the end
contactItem.Categories = category;
contact.Add(contactItem);
await context.Contacts.AddRangeAsync(contact);
await context.Categories.AddRangeAsync(category);
await context.SaveChangesAsync();
}
}
}
Test Result:
Is your DataSeeder
running successfully? I modified your code according to this official documentation and it works.
DataSeeder.cs:
public class DataSeeder
{
public static async Task InitializeAsync(IServiceProvider serviceProvider)
{
var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>());
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
string? appUserId;
List<Contact> contact = new List<Contact>();
List<Category> category = new List<Category>();
using (context)
{
if (!context.Users.Any())
{
var demoUser = new AppUser()
{
UserName = "demouser@mail.com",
Email = "demouser@mail.com",
FirstName = "Demo",
LastName = "User",
EmailConfirmed = true
};
await userManager.CreateAsync(demoUser, "Aa12345!");
}
await context.SaveChangesAsync();
appUserId = (await userManager.FindByEmailAsync("demouser@mail.com")).Id;
try
{
if (!context.Contacts.Any())
{
contact = new List<Contact>()
{
new Contact()
{
FirstName = "John",
LastName = "Dickens",
City = "Concord",
Email = "john@mail.com",
BirthDate = new DateTime(1994,8,20),
PhoneNumber = "(212)345-8587",
DateCreated = new DateTime(2023,5,20),
AppUserId = appUserId,
}
};
}
await context.Contacts.AddRangeAsync(contact);
await context.SaveChangesAsync();
}
catch (Exception ex)
{
Console.WriteLine("************* ERROR *************");
Console.WriteLine("Error Seeding Contacts.");
Console.WriteLine(ex.Message);
Console.WriteLine("***********************************");
throw;
}
if (!context.Categories.Any())
{
category = new List<Category>()
{
new Category()
{
AppUserId = appUserId,
Name = "_UnCategorized",
},
new Category()
{
Name = "Friend",
AppUserId = appUserId
},
new Category()
{
Name = "Colleague",
AppUserId = appUserId
},
new Category()
{
Name = "Vendor",
AppUserId = appUserId
},
new Category()
{
Name = "Contractor",
AppUserId = appUserId
},
new Category()
{
Name = "Send Invitation",
AppUserId = appUserId
}
};
}
await context.Categories.AddRangeAsync(category);
await context.SaveChangesAsync();
}
}
}
Program.cs:
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
await DataSeeder.InitializeAsync(services);
}
After migrating and updating the database, run the project, the data is successfully added to the database.