Seeding Many to Many EF Code First Relationship - c#

There are a few other posts on this topic that I saw but I was not able to get a correct answer yet (my own fault I am sure) but I want to seed a database and I have set up a many to many relationship, but I can't figure out how to seed the second entity with the first entities id.
var users = new List<User>()
{
new User()
{
Id = 1,
FirstName = "Clark",
LastName = "Kent"
},
new User()
{
Id = 2,
FirstName = "Lex",
LastName = "Luther"
}
};
users.ForEach(p => context.Users.Add(p));
var messages = new List<Message>()
{
new Message()
{
Id = 1,
SenderId = 2,
Recipients = new List<User> { Id = 2, Id = 3} // <<< Problem is here
}
}
messages.ForEach(p => context.Messages.Add(p));
base.Seed(context);
My message class.
public class Message
{
public int Id { get; set; }
public int SenderId { get; set; }
public int RecipientsId { get; set; }
public virtual User Sender { get; set; }
public virtual ICollection<User> Recipients { get; set; }
}
My user class.
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Message> Messages { get; set; }
}
To be clear - I can properly seed users with no problem, if there are any typos or copy paste errors they aren't a big deal because I know everything is working besides being able to create a list of recipients that have already been seeded.
Thanks in advance

This is an old thread but Ive just had a similar problem so thought I would offer an answer.
You need to query your users object created above
var messages = new List<Message>()
{
new Message()
{
Id = 1,
SenderId = 2,
Recipients = new List<User>()
{
users.Single(u => u.Id == 1),
users.Single(u => u.Id == 2)
}
}

First thing i notice is that you are adding Ints to the recipients list, not user objects
What happens if you try code like this:
var users = new List<User>()
{
new User()
{
Id = 1,
FirstName = "Clark",
LastName = "Kent"
},
new User()
{
Id = 2,
FirstName = "Lex",
LastName = "Luther"
}
};
users.ForEach(p => context.Users.Add(p));
var messages = new List<Message>()
{
new Message()
{
Id = 1,
SenderId = 2,
Recipients = new List<User> {users[0],users[1] } // <<< Problem is here
}
}
messages.ForEach(p => context.Messages.Add(p));
context.SaveChanges();
or you could event try this:
var users = new List<User>()
{
new User()
{
Id = 1,
FirstName = "Clark",
LastName = "Kent"
},
new User()
{
Id = 2,
FirstName = "Lex",
LastName = "Luther"
}
};
users.ForEach(p => context.Users.Add(p));
var messages = new List<Message>()
{
new Message()
{
Id = 1,
SenderId = 2,
Recipients = new List<User> {context.Users.Where(u=>u.Id==1),context.Users.Where(u=>u.Id==1) }
}
}
messages.ForEach(p => context.Messages.Add(p));
context.SaveChanges();

Related

Copy add or combine a model

I am having problem adding to the model. I want to have a list in the Root1 or in the Viewmodel like fullname and authors list.
newModel1
AuthorsAccepted count=4
FullName = Jack
AuthorsAccepted count=4
FullName = Time Dean
With the code below it only adds the last one to the Root1. I created a a third model. Is there a way I can add to the third model both the lists or in Root1.
var fullName = "Jack";
var fullName2 = "Tim Dean";
var authors1 = new List<AuthorsAccepted1>() {
new AuthorsAccepted1(){ id = 1, Name="Bill"},
new AuthorsAccepted1(){ id = 2, Name="Steve"},
new AuthorsAccepted1(){ id = 3, Name="jon"},
new AuthorsAccepted1(){ id = 4, Name="nick"}
};
var authors2 = new List<AuthorsAccepted1>() {
new AuthorsAccepted1(){ id = 1, Name="jack"},
new AuthorsAccepted1(){ id = 2, Name="tim"},
new AuthorsAccepted1(){ id = 3, Name="james"},
new AuthorsAccepted1(){ id = 4, Name="mary"}
};
var newModel1 = new Root1();
newModel1.FullName = fullName;
newModel1.AuthorsAccepted = authors1;
var newModel2 = new Root1();
newModel2.FullName = fullName2;
newModel2.AuthorsAccepted = authors2;
}
}
public class Root1
{
public string FullName { get; set; }
public List<AuthorsAccepted1> AuthorsAccepted { get; set; }
}
public class AuthorsAccepted1
{
public string Name { get; set; }
public int id { get; set; }
}
public class Viewmodel
{
public Root1 AllModel { get; set; }
}

Seeding with ASP.NET Core with one to many relationships

I am trying to create a seed for my database in ASP.NET Core but I am having trouble with the relationships between the models. I have 3 models with 2 relationships. I have the following models:
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public Guid ID { get; set; } = Guid.NewGuid();
public Course Course { get; set; }
public Student Student { get; set; }
public Grade Grade { get; set; }
}
public class Course
{
public Guid ID { get; set; } = Guid.NewGuid();
public string Title { get; set; }
public int Credits { get; set; }
public List<Enrollment>? Enrollments { get; set; }
}
public class Student
{
public Guid ID { get; set; } = Guid.NewGuid();
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; } = DateTime.Now;
public List<Enrollment>? Enrollments { get; set; }
}
On my DBContext I try to create the seed:
List<Student> students = new List<Student>()
{
new Student {FirstName = "Jaimie", LastName = "Vos", EnrollmentDate = DateTime.Now },
new Student {FirstName = "Bas", LastName = "Milius", EnrollmentDate = DateTime.Now },
new Student {FirstName = "Rien", LastName = "Bijl", EnrollmentDate = DateTime.Now },
new Student {FirstName = "Rajeck", LastName = "Massa", EnrollmentDate = DateTime.Now }
};
modelBuilder.Entity<Student>().HasData(students);
List<Course> courses = new List<Course>()
{
new Course {Title = "Wiskunde", Credits = 20},
new Course {Title = "Nederlands", Credits = 15},
new Course {Title = "Frans", Credits = 10},
};
modelBuilder.Entity<Course>().HasData(courses);
Enrollment test = new Enrollment();
test.Grade = Grade.A;
test.Course = courses[0];
test.Student = students[1];
modelBuilder.Entity<Enrollment>().HasData(test);
But when I run this I get the error:
The seed entity for entity type 'Enrollment' cannot be added because no value was provided for the required property 'CourseID'.
I followed the documentation for relations, does someone know a way to fix this issue?
I've found that you can just create a new object (not specifying the type) and give it a property specifying the related ID. So you should be able to do something like the following:
modelBuilder.Entity<Course>().HasData(new [] {
new { Title = "Frans", Credits = 10, Id = <courseGUID1> },
new { Title = "Nederlands", Credits = 15, Id = <courseGUID2> }
});
modelBuilder.Entity<Enrollment>().HasData(new [] {
new { Grade = Grade.A, CourseId = <courseGUID1>, StudentId = <studentGUID1> }
});
In conclusion, I hate seed data, but have used it a fair amount and the above solution seems to get me by. It's not intuitive and would be nice if the code you had worked!
I would assume that the IDs should be generated when created, but obviously that doesn't work.
PS: If using auto-incrementing integer IDs, I usually just use negative integers to avoid conflict with the generated IDs.

Query mongo document array

I have the next mongo document structure :
_id
-countryCode
-keywordID
-name
-displayName
-categories:[Array]
-_id
-name
-position
-canonical
I would like to get all the keywords that are in a specific category only knowing the category's ID. I am using the mongo C# driver but don't know how could I check what's inside that array.
I would like to send a list with the category ID's and get back all the keywords that have a category from that list.
public async Task<List<Keyword>> GetKeywords(List<long> keywordCatIds, string countryCode)
{
var mongoCollection = MongoDatabase.GetCollection<Keyword>("Keywords");
try
{
FilterDefinition<Keyword> mongoFilter = Builders<Keyword>.Filter.In(c=>c.Categories, keywordCatIds);
return await mongoCollection.Find(mongoFilter,null).ToListAsync<Keyword>();
}
catch (Exception ex)
{
Logger.Error(ex, "Multiple ids for Country Code: {0}, ids: {1}", countryCode, string.Join(',', keywordCatIds.Select(s => s)));
return null;
}
}
Your In function looks like a "categories._id" filter in normal mongoDB. Which transitions into an ElemMatch. I created a project which fills the db, than selects
all the keywords that are in a specific category only knowing the category's ID
public class CustomID
{
public string CountryCode { get; set; }
public long KeywordId { get; set; }
public string Name { get; set; }
}
public class Keyword
{
[BsonId]
public CustomID Id { get; set; }
public List<Category> Categories { get; set; }
}
public class Category
{
[BsonId]
public long Id { get; set; }
public string Name { get; set; }
public int Position { get; set; }
}
internal class Program
{
public static IMongoDatabase MongoDatabase { get; private set; }
public static async Task Main()
{
var conventionPack = new ConventionPack
{
new CamelCaseElementNameConvention()
};
ConventionRegistry.Register(
"CustomConventionPack",
conventionPack,
t => true);
var client = new MongoClient();
MongoDatabase = client.GetDatabase("SO");
var ret = await GetKeywords(new List<long> {1L, 2L}, "HU-hu");
// ret is A and B. C is filtered out because no category id of 1L or 2L, D is not HU-hu
}
public static async Task<List<Keyword>> GetKeywords(List<long> keywordCatIds, string countryCode)
{
var mongoCollection = MongoDatabase.GetCollection<Keyword>("keywords");
// be ware! removes all elements. For debug purposes uncomment>
//await mongoCollection.DeleteManyAsync(FilterDefinition<Keyword>.Empty);
await mongoCollection.InsertManyAsync(new[]
{
new Keyword
{
Categories = new List<Category>
{
new Category {Id = 1L, Name = "CatA", Position = 1},
new Category {Id = 3L, Name = "CatC", Position = 3}
},
Id = new CustomID
{
CountryCode = "HU-hu",
KeywordId = 1,
Name = "A"
}
},
new Keyword
{
Categories = new List<Category>
{
new Category {Id = 2L, Name = "CatB", Position = 2}
},
Id = new CustomID
{
CountryCode = "HU-hu",
KeywordId = 2,
Name = "B"
}
},
new Keyword
{
Categories = new List<Category>
{
new Category {Id = 3L, Name = "CatB", Position = 2}
},
Id = new CustomID
{
CountryCode = "HU-hu",
KeywordId = 3,
Name = "C"
}
},
new Keyword
{
Categories = new List<Category>
{
new Category {Id = 1L, Name = "CatA", Position = 1}
},
Id = new CustomID
{
CountryCode = "EN-en",
KeywordId = 1,
Name = "EN-A"
}
}
});
var keywordFilter = Builders<Keyword>.Filter;
var categoryFilter = Builders<Category>.Filter;
var mongoFilter =
keywordFilter.ElemMatch(k => k.Categories, categoryFilter.In(c => c.Id, keywordCatIds)) &
keywordFilter.Eq(k => k.Id.CountryCode, countryCode);
return await mongoCollection.Find(mongoFilter).ToListAsync();
}
}

Linq : Comparing 1 Child Collection to (Aggregated) ChildCollection(s)

I have a Linq question: (DotNet Framework 4.0)
I have the following classes:
public class Employee
{
public Guid? EmployeeUUID { get; set; }
public string SSN { get; set; }
}
public class JobTitle
{
public Guid? JobTitleSurrogateKey { get; set; }
public string JobTitleName { get; set; }
}
public class EmployeeToJobTitleMatchLink
{
public EmployeeToJobTitleMatchLink()
{
this.TheJobTitle = new JobTitle() { JobTitleSurrogateKey = Guid.NewGuid(), JobTitleName = "SomeJobTitle:" + Guid.NewGuid().ToString("N") };
}
public Guid LinkSurrogateKey { get; set; }
/* Related Objects */
public Employee TheEmployee { get; set; }
public JobTitle TheJobTitle { get; set; }
}
public class Organization
{
public Organization()
{
this.Links = new List<EmployeeToJobTitleMatchLink>();
}
public int OrganizationSurrogateKey { get; set; }
public ICollection<EmployeeToJobTitleMatchLink> Links { get; set; }
}
In my code below, I can compare 2 child-collections and get the results I need (in "matches1".
Here I am using the "SSN" string property to compare and find the overlaps. And the Console.Write for matches1 works as I expect.
What I don't know how to do is compare the first child collection (org10) to all the children in (allOtherOrgsExceptOrg10 (all the Organizations and all the Links of these Organizations )
The commented out code shows kinda what I'm trying to do, one of my many feeble attempts today.
But basically, match2 would be populated with all the SSN overlaps...but comparing org10 with allOtherOrgsExceptOrg10, all their "Links", and their Employee.SSN's.
org10 overlaps with org20 with "AAA", so match2 would contain "AAA". and org10 overlaps with org30 with "BBB" so match2 would contain "BBB".
Organization org10 = new Organization();
org10.OrganizationSurrogateKey = 10;
Employee e11 = new Employee() { SSN = "AAA", EmployeeUUID = new Guid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA") };
EmployeeToJobTitleMatchLink link11 = new EmployeeToJobTitleMatchLink();
link11.TheEmployee = e11;
org10.Links.Add(link11);
Employee e12 = new Employee() { SSN = "BBB", EmployeeUUID = new Guid("BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB") };
EmployeeToJobTitleMatchLink link12 = new EmployeeToJobTitleMatchLink();
link12.TheEmployee = e12;
org10.Links.Add(link12);
Organization org20 = new Organization();
org20.OrganizationSurrogateKey = 20;
Employee e21 = new Employee() { SSN = "AAA", EmployeeUUID = new Guid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA") };
EmployeeToJobTitleMatchLink link21 = new EmployeeToJobTitleMatchLink();
link21.TheEmployee = e21;
org20.Links.Add(link21);
Employee e22 = new Employee() { SSN = "CCC", EmployeeUUID = new Guid("CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC") };
EmployeeToJobTitleMatchLink link22 = new EmployeeToJobTitleMatchLink();
link22.TheEmployee = e22;
org20.Links.Add(link22);
Organization org30 = new Organization();
org30.OrganizationSurrogateKey = 30;
Employee e31 = new Employee() { SSN = "BBB", EmployeeUUID = new Guid("BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB") };
EmployeeToJobTitleMatchLink link31 = new EmployeeToJobTitleMatchLink();
link31.TheEmployee = e31;
org30.Links.Add(link31);
Employee e32 = new Employee();
e32.SSN = "ZZZ";
EmployeeToJobTitleMatchLink link32 = new EmployeeToJobTitleMatchLink();
link32.TheEmployee = e32;
org30.Links.Add(link32);
IList<Organization> allOtherOrgsExceptOrg10 = new List<Organization>();
/* Note, I did not add org10 here */
allOtherOrgsExceptOrg10.Add(org20);
allOtherOrgsExceptOrg10.Add(org30);
IEnumerable<EmployeeToJobTitleMatchLink> matches1 =
org10.Links.Where(org10Link => org20.Links.Any(org20Link => org20Link.TheEmployee.SSN.Equals(org10Link.TheEmployee.SSN, StringComparison.OrdinalIgnoreCase)));
IEnumerable<EmployeeToJobTitleMatchLink> matches2 = null;
//org10.Links.Where(org10Link => ( allOtherOrgs.Where ( anyOtherOrg => anyOtherOrg.Links.Any(dbSideChild => dbSideChild.TheEmployee.SSN == org10Link.TheEmployee.SSN)) );
if (null != matches1)
{
foreach (EmployeeToJobTitleMatchLink link in matches1)
{
Console.WriteLine(string.Format("matches1, SSN = {0}", link.TheEmployee.SSN));
}
}
if (null != matches2)
{
foreach (EmployeeToJobTitleMatchLink link in matches2)
{
Console.WriteLine(string.Format("matches2, SSN = {0}", link.TheEmployee.SSN));
}
}
matches2 =
allOtherOrgsExceptOrg10.SelectMany(x => x.Links)
.Where(x => org10.Links.Select(o => o.TheEmployee.SSN).Contains(x.TheEmployee.SSN));
You can use the SelectMany on the allOther collection to select all Links over all org's. Then check if any SSN is inside the org10 List.
See: http://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany(v=vs.100).aspx
You can use SelectMany to flatten out the collection and then use it just like you have for matches1
IEnumerable<EmployeeToJobTitleMatchLink> matches2 =
org10.Links.Where(
org10Link =>
allOtherOrgsExceptOrg10.SelectMany(allOtherOrgs => allOtherOrgs.Links).Any(
anyOtherLink =>
anyOtherLink.TheEmployee.SSN.Equals(org10Link.TheEmployee.SSN, StringComparison.OrdinalIgnoreCase)));
The SelectMany will make it seem like one IEnumerable instead of and IEnumerable of an IEnumerable.

Flatten LINQ Collection

I have had a look at this Flatten LINQ collection object with nested object collections but it doesn't quite do it for me.
I know there is a lot of code in this post but it's mostly just data to give you the idea of what I'm looking at developing.
if you look at the classes below, I am trying to come up with a way to flatten the result of a search against the file.
So i need to end up with a single flattened record which looks like (the pipes are there to show delimination of a field only)
fileId | FileContact1FirstName | FileContact1LastName | FileContact2FirstName etc | FileClient1FirstName | FileClient1LastName | FileClient1IsNominee | FileClient1IsPrimary | FileClient2FirstName etc....
Any idea on how I can do this without looping through each Contact and Client?
I have these classes of sorts in my edmx;
class File
{
public int fileId { get; set; }
public List<FileContact> fileContacts { get; set; }
public List<FileClient> fileClients { get; set; }
}
class FileContact
{
public Contact contact { get; set; }
}
class FileClient
{
public Contact contact { get; set; }
public bool IsNominee { get; set; }
public bool IsPrimary { get; set; }
}
class Contact
{
public int id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
And this this as the data simply for testing.
static void FillData()
{
thisFile = new File { fileId = 1, fileContacts = new List<FileContact>(), fileClients = new List<FileClient>() };
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Andrew", lastName = "Albino" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Bob", lastName = "Bush" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Cathy", lastName = "Conti" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Drew", lastName = "Dram" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Edward", lastName = "Eliston" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Frank", lastName = "Fashion" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Graham", lastName = "Grape" } });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Harry", lastName = "Who didn't" }, IsNominee = true, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Indigo", lastName = "Ignacio" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Julie", lastName = "Juniper" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Kelly", lastName = "Keilor" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Liam", lastName = "Loser" }, IsNominee = false, IsPrimary = true });
}
}
This will get you an IEnumerable<string> that contains the properties in the order you specified:
var flattened = new string[] { thisFile.fileId.ToString() }
.Concat(
thisFile.fileContacts
.SelectMany(fc => new string[]
{
fc.contact.firstName,
fc.contact.lastName
}))
.Concat(
thisFile.fileClients
.SelectMany(fc => new string[]
{
fc.contact.firstName,
fc.contact.lastName,
fc.IsNominee.ToString(),
fc.IsPrimary.ToString()
}));
Example: http://ideone.com/Mvc7M
Have a look at SelectMany.

Categories

Resources