How flatten a list with many arrays inside using LINQ - c#

I am finding this very hard to understand and where to start, so I was hoping that some one would be able to point in the correct direction. I have a list(customers) inside which there are arrays/lists. Basically I want to flatten all the results of the list into a flat version if the list.
public class Customer : EntityBase
{
public Phonenumber[] PhoneNumbers { get; set; }
public Contact BillToContact { get; set; }
public Terms Terms { get; set; }
}
public class Contact
{
public Phonenumber[] PhoneNumbers { get; set; }
public Address Address { get; set; }
public Key Key { get; set; }
public string CompanyName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
}
public class Phonenumber
{
public string Number { get; set; }
public int Key { get; set; }
}
public class Terms
{
public int DueDays { get; set; }
public int DiscountDays { get; set; }
}
public class Address
{
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public abstract class EntityBase
{
public Guid Id { get; set; }
public string Status { get; set; }
public int Rev { get; set; }
}
I have tried many approaches and just keep getting more confused. So if anyone could help or even point me in the right direction I would be extremely grateful. below is one of the approaches I have tried.
public IEnumerable<Customer> Find (Func<Customer , bool> predicate) {
foreach (var p in Customer.SelectMany(p => p)) {
if(predicate(p)) {
yield return p;
}
}
}
I am Deserializing a jason string into a list but then want to display in a datagrid, but igGrid does not support binding to nested(complex) properties. So I need to flatten the list so that there is no sub levels of the list.

To select an array of PhoneNumber from List<Customer> use SelectMany:
List<Customer> customers = [data];
PhoneNumber phoneNumbers = customers.SelectMany(x=>x.PhoneNumbers).ToArray();

It's not clear at all from your question what output you actually want. Do you just want a list of all the phone numbers? Or do you want to preserve the other Customer information, such that you get multiple instances of the Customer information, each instance with a separate phone number?
You can accomplish the former with something like this:
IEnumerable<Phonenumber> numbers =
customers.SelectMany(
customer => customer.PhoneNumbers
.Concat(BillToContact.PhoneNumbers));
If you only want the Customer.PhoneNumbers numbers and not those in the BillToContact object, just leave the .Concat(BillToContact.PhoneNumbers) out of the above.
If you want to preserve one or more values from the original Customer object, you can do something like this:
var numbers = customers.SelectMany(
customer => customer.PhoneNumbers.Select(
number => new
{
Number = number,
FirstName = customer.BillToContact.FirstName,
Email = customer.BillToContact.Email
}));
The above will generate an enumeration of anonymous type objects, each having a single phone number, along with the corresponding FirstName and Email values from the associated Contact object. You can of course mix and match (e.g. use .Concat(...) to include phone numbers from the BillToContact object), and include whichever specific Customer or Contact members you want.

Related

LINQ Projection and loading child objects

Having an issue with projection and getting child objects to load. The following is simplified code to represent the logic I'm trying to implement, not the actual code.
public class TicketItem
{
public int TicketItemId { get; set; }
public string TicketReason { get; set; }
public Station Station { get; set; }
public TicketOwner TicketOwner { get; set; }
}
public class Station
{
public int StationId { get; set; }
public string Name { get; set; }
}
public class TicketOwner
{
public int TicketOwnerId { get; set; }
public Employee Employee { get; set; }
public Organization Organization { get; set; }
}
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Organization
{
public int OrganizationId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class CommonReasons
{
public int CommonReasonId { get; set; }
public string Reason { get; set; }
}
public TicketItem GetById(int id)
{
var query = from i in _dataContext.TicketItems
.Include("Station")
.Include("TicketOwner.Employee")
.Include("TicketOwner.Organization")
join r in _dataContext.CommonReasons on i.TicketReason equals r.CommonReasonId.ToString() into r1
from r2 in r1.DefaultIfEmpty()
where i.TicketItemId == id
select new TicketItem {
TicketItemId = i.TicketItemId,
TicketReason = r2.Reason == null ? i.Reason : r2.Reason,
Station = i.Station,
TicketOwner = i.TicketOwner
};
return query
.AsNoTracking()
.FirstOrDefault();
}
Most the code is self-explanatory. The part that is indirectly causing the trouble would be the relationship between TicketItem.TicketReason property (a string) and the CommonReasons entity. From the user interface side, the end-user has an input field of "Reason", and they can select from "common" reasons or input an adhoc reason. They original developer chose to have the TicketReason property contain either the key ID from the CommonReasons table (if the user selected from drop-down) or the adhoc reason typed in.
So, to handle this logic in the linq query, the only way I have found is to do a left join between TicketItem.TicketReason and CommonReasons.CommonReasonId, then use projection to modify the TicketReason column returning either the common reason text or adhoc text. If there is a different way to do this that would get me around the trouble I'm having with projection/include, I'm all ears.
For the "reason" logic, this query works, returning the proper text. The trouble is that none of the "grand-child" objects are returning, i.e. TicketItem.TicketOwner.Employee, TicketItem.TicketOwner.Organization. How do I get those objects to return also?
Changing the structure of the tables would be an absolute last resort, just based on the amount of code that would have to change. There are other spots in the code that are using the above logic but don't need the child objects.
Any help would be appreciated. Hope I've explained enough.

Project data with children and grandchildren entities

I'm trying to get data in a suitable format for an api
What I would like is
Place
--Rating1
---RatingImage1.1
---RatingImage1.2
---UserName
---UserId
--Rating2
---RatingImage2.1
---RatingImage2.2
---UserName
---UserId
In a nutshell im trying to fetch a place, with its ratings(and rating images), with the names of the users who did the rating given the googlePlaceId
Tried this but it goes and does some circular fetching where once it fetches the user it then fetches the user rating and the response becomes massive
context.Places
.Include(x => x.Ratings.Select(y => y.User))
.Include(x => x.Ratings.Select(c => c.RatingImages))
.Single(x => x.GooglePlaceId == googlePlaceId);
I think projection or linq joins must be the way, but i havent had any success yet.
here are my POCOS
Place Poco
public class Place
{
public Place()
{
Ratings = new List<Rating>();
Favourites = new List<Favourite>();
}
public int Id { get; set; }
public string Name { get; set; }
public string GooglePlaceId { get; set; }
public ICollection<Rating> Ratings { get; set; }
public ICollection<Favourite> Favourites { get; set; }
}
Rating POCO
public class Rating
{
public Rating()
{
RatingImages = new List<RatingImage>();
}
public int Id { get; set; }
public float RatingValue { get; set; }
public string RatingComment { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
public string UserId { get; set; }
public AspNetUser User { get; set; }
public ICollection<RatingImage> RatingImages { get; set; }
}
User POCO
public partial class AspNetUser
{
public string UserName { get; set; }
public string Id { get; set; }
// the rest of the fields are omitted
}
Although you've omitted the definition of AspNetUser, I'm guessing it has a navigation property back to Ratings. Is this required anywhere else in your application? It won't affect the structure of your database, and removing it would allow your projection to work exactly as you've got it here. You'd still be able to display all ratings by a single user using a separate query - you've got to optimise for your most common scenario though.

Object optimization and grouping

I have objects that are defined this way:
class BFull
{
public string ImageID { get; set; }
public List<string> Tapes { get; set; }
}
class OptSet
{
public int SetID { get; set; }
public List<string> Tapes { get; set; }
public List<string> Images { get; set; }
}
The BFull object is DB defined and I can't alter it much. The second one is the wanted result of my actions. And the problem is:
I need to optimize those BFulls into OptSets with a limitation that any given OptSet can have max 24 distinct tapes and consist only complete BFulls. Can anybody help how to achieve that?

View Model implementation - less fields than in entities

I have two entities: Person and Quote (in one to many relationship)
Person:
public class Person
{
public int PersonID { get; set; }
[Required]
[StringLength(20]
public string Name { get; set; }
[StringLength(30]
public string Relation { get; set; }
public byte[] Image { get; set; }
[StringLength(50)]
public string ImageMimeType { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
}
Quote:
public class Quote
{
public int QuoteID { get; set; }
public int PersonID { get; set; }
[Required]
[StringLength(200)]
public string QuoteName { get; set; }
[StringLength(400)]
public string Context { get; set; }
public DateTime? Date { get; set; }
public virtual Person Person { get; set; }
}
I want to make a ViewModel for displaying quotes in short format - I need just a few properties - Person Name, QuoteName and Person Image. I could do something casual like they're showing in every ASP.NET MVC tutorial:
public class QuoteViewModel
{
public IEnumerable<Quote> Quotes { get; set; }
}
Is there a better way rather than creating IEnumerable with type of Quote and loading all properties?
How about creating QuoteShort model and making QuoteViewModel as IEnumerable<QuoteShort> QuotesShort.
In controller I would map every 3 fields from repository to QuoteShort and add it to QuotesShort IEnumerable (even though I don't know how to persist them to QuotesShort IEnumerable )
Some examples appreciated.
You can make a QuoteShort ViewModel with just the few properties you need, and then have your view expect IEnumerable<QuoteShort> as its model. You don't necessarily have to wrap that up in another container.
If you have this:
public class QuoteShort{
public Person Person {get;set;}
public string Name {get; set;}
// etc
}
You can do this in the controller:
var quotes = //however you get your list of quotes
var model = (from q in quotes select new QuoteShort
{ Person = q.Person, Name = q.Name /*etc*/ }).ToList();
return View(model);
What about something like
public class QuotesShortViewModel
{
public IEnumerable<QuoteShortViewModel> QuotesShort { get; set; }
}
public class QuoteShortViewModel
{
// ... the properties you need
}
Create a View that receives a QuotesShortViewModel and iterates through the list, rendering the short quotes as it pleases you.
AutoMapper is useful to map between Models and ViewModels in your controllers.

Create Index in RavenDB

I have the following document :
public class Cars
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
//collection of car price id
public List<string> CarsPriceIds { get; set; }
}
public class CarsPriceIds
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public List<string> CarTypesIds { get; set; }
}
public class CarTypes
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
}
My input is Car.
I need to display in my view
the car name and the CarType.Name match to CarsPriceIds
I start write my index and got stuck. Here is my index
new IndexDefinitionBuilder<Cars>
{
Map = docs => from doc in docs
from carsPriceId in doc.CarsPriceIds
from carTypes in ????
select new { doc.Name}
}
I can't continue!!!
What next??
Any help please
Regards
You have probably chosen the wrong model, because it seems that CarPrice and CarType have no meaning outside Car. Therefore they should be inside the Car document. Generally your documents are equal to your aggregate roots in DDD terms.
However, if you really want to stick with this model and just want an index that can join your CarPrices and CarTypes, take a look at this: http://ayende.com/blog/4661/ravendb-live-projections-or-how-to-do-joins-in-a-non-relational-database
You probably want to look at live projections:
http://ayende.com/blog/4661/ravendb-live-projections-or-how-to-do-joins-in-a-non-relational-database

Categories