Flatten LINQ Collection - c#

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.

Related

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();
}
}

Entity Framework Seed method not running

I'm having some trouble getting the Seed method of EF to run. I've run update-database in the PMC - but no effect on the DB. Here's the method:
public class PhilosopherInitialiser : System.Data.Entity.DropCreateDatabaseIfModelChanges<PhilosopherContext>
{
protected override void Seed(PhilosopherContext context)
{
var philosophers = new List<Philosopher>{
new Philosopher {
FirstName = "Bertrand",
LastName = "Russell",
DateOfBirth = DateTime.Parse("1872-05-18"),
DateOfDeath = DateTime.Parse("1970-02-02"),
IsAlive = false,
NationalityID = 1,
AreaID = 7,
Description = "Here's some text about Bertrand Russell"
},
new Philosopher {
FirstName = "Immanuel",
LastName = "Kant",
DateOfBirth = DateTime.Parse("1724-04-22"),
DateOfDeath = DateTime.Parse("1804-02-12"),
IsAlive = false,
NationalityID = 3,
AreaID = 1,
Description = "Here's some text about Immanuel Kant"
},
new Philosopher {
FirstName = "John",
LastName = "Rawls",
DateOfBirth = DateTime.Parse("1921-02-21"),
DateOfDeath = DateTime.Parse("2002-11-24"),
IsAlive = false,
NationalityID = 9,
AreaID = 3,
Description = "Here's some text about John Rawls"
}
};
philosophers.ForEach(p => context.Philosophers.Add(p));
context.SaveChanges();
var nationalities = new List<Nationality>
{
new Nationality { Name = "English" },
new Nationality { Name = "Scotish" },
new Nationality { Name = "German" },
new Nationality { Name = "French" },
new Nationality { Name = "Greek" },
new Nationality { Name = "Italian" },
new Nationality { Name = "Spanish" },
new Nationality { Name = "Russian" },
new Nationality { Name = "American" }
};
nationalities.ForEach(n => context.Nationalities.Add(n));
context.SaveChanges();
var areas = new List<Area>{
new Area { Name = "Metaphysics" },
new Area { Name = "Existentialism" },
new Area { Name = "Political philosophy" },
new Area { Name = "Philosophy of the mind" },
new Area { Name = "Aesthetics" },
new Area { Name = "Social philosophy" },
new Area { Name = "Logic" },
new Area { Name = "Moral philosophy" },
new Area { Name = "Epistemology" }
};
areas.ForEach(a => context.Areas.Add(a));
context.SaveChanges();
var books = new List<Book>
{
new Book {
Title = "The impact of science on society",
PhilosopherID = 1,
AreaID = 6
},
new Book {
Title = "The analysis of mind",
PhilosopherID = 1,
AreaID = 4
},
new Book {
Title = "Marriage and morals",
PhilosopherID = 1,
AreaID = 8
},
new Book{
Title = "Critique of pure reason",
PhilosopherID = 2,
AreaID = 9
},
new Book{
Title = "The metaphysics of morals",
PhilosopherID = 2,
AreaID = 8
},
new Book{
Title = "A theory of justice",
PhilosopherID = 3,
AreaID = 3
}
};
books.ForEach(b => context.Books.Add(b));
context.SaveChanges();
}
}
}
And here's my PhilosopherContext class:
public class PhilosopherContext : DbContext
{
public PhilosopherContext() : base("PhilosopherContext")
{
}
public DbSet<Philosopher> Philosophers { get; set; }
public DbSet<Area> Areas { get; set; }
public DbSet<Nationality> Nationalities { get; set; }
public DbSet<Book> Books { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Book>()
.HasRequired(p => p.Philosopher)
.WithMany()
.HasForeignKey(p => p.PhilosopherID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Philosopher>()
.Property(p => p.DateOfBirth)
.HasColumnType("datetime2");
modelBuilder.Entity<Philosopher>()
.Property(p => p.DateOfDeath)
.HasColumnType("datetime2");
}
}
}
Inside the Web.Config file I'm using initialising the DB here:
<contexts>
<context type="PhilosophersLibrary.DAL.PhilosopherContext, PhilosophersLibrary">
<databaseInitializer type="PhilosophersLibrary.DAL.PhilosopherInitialiser, PhilosophersLibrary" />
</context>
</contexts>
Does anyone have any suggestions? I feel that the method might not be called.
UPDATE
I seem to be making progress. The Areas and Nationalities tables are being seeded with the data. But I have to comment out the Philosophers data and the Books data. Is there something wrong with my data model?
public class Book
{
public int BookID { get; set; }
public string Title { get; set; }
[Display(Name = "Philosopher")]
public int PhilosopherID { get; set; }
[Display(Name = "Area")]
public int AreaID { get; set; }
public virtual Philosopher Philosopher { get; set; }
public virtual Area Area { get; set; }
}
public class Philosopher
{
// <className>ID pattern causes property to be primary key
public int PhilosopherID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Display(Name = "Date of birth")]
public DateTime DateOfBirth { get; set; }
[Display(Name = "Date of death")]
public DateTime DateOfDeath { get; set; }
public Boolean IsAlive { get; set; }
public string Description { get; set; }
// Foreign keys have corresponding navigation properties
// <NavigationProperty>ID naming convention cause EF to identify foreign keys
public int NationalityID { get; set; }
public int AreaID { get; set; }
// Navigation properties - defined as virtual to use LazyLoading
// Nationality and Area have a 1 to 1 relationship with philosopher
// Books has a 1 to many relationship with philosopher
public virtual Nationality Nationality { get; set; }
public virtual Area Area { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
Try to use this:
CreateDatabaseIfNotExists<PhilosopherContext>
instead of this:
DropCreateDatabaseIfModelChanges<PhilosopherContext>
Also I would add:
public PhilosopherContext() : base("PhilosopherContext")
{
Database.SetInitializer<PhilosopherContext>(new CreateDatabaseIfNotExists<PhilosopherContext>());
}

How to order parent object by child sub object

i have a List< PaperAbstract > class. a PaperAbstract class has a set of Authors. one of the Authors has a flag IsSubmitting true. how can i order my List< PaperAbstract > by the submitting authors LastName?
public class PaperAbstract
{
public string Title { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public bool IsSubmitting { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
for example:
var paperAbstracts = new List<PaperAbstract>();
paperAbstracts.Add(new PaperAbstract
{
Title = "Abstract 2",
Authors = new List<Author>
{
new Author { IsSubmitting = false, FirstName = "F5", LastName = "L5"},
new Author { IsSubmitting = true, FirstName = "F6", LastName = "L6"}
}
});
paperAbstracts.Add(new PaperAbstract
{
Title = "Abstract 3",
Authors = new List<Author>
{
new Author { IsSubmitting = true, FirstName = "F1", LastName = "L1"},
new Author { IsSubmitting = false, FirstName = "F2", LastName = "L2"}
}
});
paperAbstracts.Add(new PaperAbstract
{
Title = "Abstract 1",
Authors = new List<Author>
{
new Author { IsSubmitting = false, FirstName = "F3", LastName = "L3"},
new Author { IsSubmitting = true, FirstName = "F4", LastName = "L4"}
}
});
the correct order of paperAbstracts should be Abstract 3, Abstract 1, Abstract 2.
You can use OrderBy from LINQ:
var result = input.OrderBy(x => x.Authors
.First(a => a.IsSubmitting).LastName)

Seed Data with ICollection?

I have a school project with a one to many relationship (Contact can have many Addresses). But I don't know how to seed it correctly.
In my Data models Contact has a virtual ICollection<Address> Addresses and the address object has the foreign key of ContactId.
So here is my seed data (code first) And i need to make it so when i type in the contacts last name in a search bar it will pull up all the info on that contact (address Info).
So how do i associate the info together in my seed data so when you search it pulls up what it is supposed to?
namespace Success.Data.Migrations
{
public class Seeder
{
public static void Seed(SuccessContext context,
bool seedContacts = true,
bool seedAddresses = true)
{
if (seedContacts) SeedContacts(context);
if (seedAddresses) SeedAddresses(context);
}
private static void SeedContacts(SuccessContext context)
{
context.Contacts.AddOrUpdate(l => l.LastName,
new Contact() { FullName = "Darth Vader", FirstName = "Darth", LastName = "Vader", },
new Contact() { FullName = "Luke Skywalker", FirstName = "Luke", LastName = "Skywalker", },
new Contact() { FullName = "Tony Stark", FirstName = "Tony", LastName = "Stark", },
new Contact() { FullName = "Ricky Bobby", FirstName = "Ricky", LastName = "Bobby", },
new Contact() { FullName = "Trix Rabbit", FirstName = "Trix", LastName = "Rabbit", });
context.SaveChanges();
}
private static void SeedAddresses(SuccessContext context)
{
context.Addresses.AddOrUpdate(h => h.HomeAddress,
new Address() { HomeAddress = "1300 DeathStar", BusinessAddress = "444 Imperial Fleet", PoBox = "PO Box 1335", ContactId = 1, },
new Address() { HomeAddress = "1997 Endor", BusinessAddress = "448 Rebel Fleet", PoBox = "PO Box 1339", ContactId = 2, },
new Address() { HomeAddress = "1224 Malibu Point", BusinessAddress = "657 Stark Industries", PoBox = "PO Box 1337", ContactId = 3, },
new Address() { HomeAddress = "9978 Fast LN.", BusinessAddress = "532 NASCAR Race Track", PoBox = "PO Box 1333", ContactId = 4, },
new Address() { HomeAddress = "9864 Cerial Box LN", BusinessAddress = "8432 Kellog Dr.", PoBox = "PO Box 1338", ContactId = 5, });
context.SaveChanges();
}
}
}
You could have another method that seeds both the contacts and address. You will need an extra if/else switch
if (seedContacts && seedAddresses)
{
SeedContactsAndAddress(context);
}
else
{
if (seedContacts) SeedContacts(context);
if (seedAddresses) SeedAddresses(context);
}
And the SeedContactsAndAddress Method would look like this:
private static void SeedContactsAndAddress(StoreContext context)
{
// Each Address, which I believe is a collection in this case, but there is only
// one, will have to be created and added to each contact.
var addressesForDarthVader = new List<Address>
{
new Address { HomeAddress = "1300 DeathStar", BusinessAddress = "444 Imperial Fleet", PoBox = "PO Box 1335" }
// Add more addresses for Darth Vader if you need to
};
// Rinse and repeat for the other contacts;
context.Contacts.AddOrUpdate(l => l.LastName,
new Contact() { FullName = "Darth Vader", FirstName = "Darth", LastName = "Vader", Addresses = addressesForDarthVader },
new Contact() { FullName = "Luke Skywalker", FirstName = "Luke", LastName = "Skywalker", },
new Contact() { FullName = "Tony Stark", FirstName = "Tony", LastName = "Stark", },
new Contact() { FullName = "Ricky Bobby", FirstName = "Ricky", LastName = "Bobby", },
new Contact() { FullName = "Trix Rabbit", FirstName = "Trix", LastName = "Rabbit", });
context.SaveChanges();
}

Seeding Many to Many EF Code First Relationship

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();

Categories

Resources