I have some collections:
Dictionary<InvoceHeader, List<InvoiceHierarchy>> where
public class InvoceHeader
{
public int InvoiceId { get; set; }
public string DocumentNumber { get; set; }
public string DocumentDate { get; set; }
public string DocumentReference { get; set; }
}
public class InvoiceHierarchi
{
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string Certificate { get; set; }
public string Description { get; set; }
}
I need to write a LINQ request to get such a collection from the original collection --> Dictionary<string, List<string>>, where key --> InvoceHeader.DocumentNumber, value --> List two elements from InvoiceHierarchi such as the SerialNumber and Certificate.
Dictionary<string, List<string>> converted = original
.ToDictionary(
kvp => kvp.Key.DocumentNumber,
kvp => kvp.Value.Select(ih => ih.SerialNumber + ih.Certificate).ToList());
Try following :
class Program
{
static void Main(string[] args)
{
List<InvoceHeader> headers = new List<InvoceHeader>();
List<InvoiceHierarchi> hierarchi = new List<InvoiceHierarchi>();
var query = (from header in headers
join hierarch in hierarchi on header.DocumentNumber equals hierarch.Certificate
select new { header = header, hierarch = hierarch }
).ToList();
Dictionary<string, object> dict = query
.GroupBy(x => x.header.DocumentNumber, y => new { header = y.header, hierachi = y.hierarch })
.ToDictionary(x => x.Key, y => (object)y.ToList());
}
}
public class InvoceHeader
{
public int InvoiceId { get; set; }
public string DocumentNumber { get; set; }
public string DocumentDate { get; set; }
public string DocumentReference { get; set; }
}
public class InvoiceHierarchi
{
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string Certificate { get; set; }
public string Description { get; set; }
}
That should be the query that you are looking for, only that you can replace the anonymous types that I am using in the query with a custom type that you created.
var newCollection = originalCollection.Select(x => new
{
Key = x.Key.DocumentNumber,
Value = x.Value.Select(y => new
{
y.SerialNumber,
y.Certificate
}).ToList()
});
If you want again to convert it to Dictionary, you can also add that line:
var newDictionary = newCollection.ToDictionary(x => x.Key, y => y.Value);
Related
I have a stored proc returning a datatable using a stored procedure. I am able to convert the it to an object using the following code
outgoingPaymentData.AsEnumerable().Select(x => new OutgoingPaymentApprovalDetails() { });
Here is my OutgoingPaymentApprovalDetails class
public class OutgoingPaymentApprovalDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public string DocumentId { get; set; }
public string DebtorName { get; set; }
public string Currency { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Now, instead of a flat list, I need to add heirarchy, to select this one object to 3 objects.
Classes as under:
public class OriginatorDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public List<DocumentDetails> DocumentDetails { get; set; }
}
public class DocumentDetails
{
public string DocumentId { get; set; }
public List<TransactionDetails> TransactionDetails { get; set; }
}
public class TransactionDetails
{
public string Amount { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Basically, All Documents of a particular Originator have to be in the list of DocumentDetails and all TransactionDetails of a particular document have to be in that list.
One way is to create a dictionary and add stuff in it and finally create an object. I was wondering if there was a more abbreviated and efficient way to do something like this.
TIA
You can do the grouping of retrieved records of OutgoingPaymentApprovalDetails using Linq to create the nested object of OriginatorDetails collection.
see below code
var originalDetails = inputs.GroupBy(g => g.OriginatorId)
.Select(g => new OriginatorDetails()
{
OriginatorId = g.Key,
Name = g.First().Name,
DocumentDetails = g.GroupBy(d => d.DocumentId)
.Select(d => new DocumentDetails()
{
DocumentId = d.Key,
TransactionDetails = d.Select(t => new TransactionDetails()
{
DebitAccountNo = t.DebitAccountNo,
CreditAccountNo = t.CreditAccountNo,
Amount = t.Amount.ToString()
}).ToList()
})
.ToList()
});
Check the created https://dotnetfiddle.net/FCA7Qc to demostrate your scenario.
Try this code:
Basically you need to group 2 times, first time by OriginatorId and Name and then by DocumentId like this:
var result = list.GroupBy(c => new {c.OriginatorId, c.Name})
.Select(g => new OriginatorDetails()
{
Name = g.Key.Name,
OriginatorId = g.Key.OriginatorId,
DocumentDetails = g
.GroupBy(dd => dd.DocumentId)
.Select(dd => new DocumentDetails()
{
DocumentId = dd.Key,
TransactionDetails = dd.ToList()
.Select(td => new TransactionDetails()
{
Amount = td.Amount.ToString(),
CreditAccountNo = td.CreditAccountNo,
DebitAccountNo = td.DebitAccountNo
}).ToList()
}).ToList()
}).ToList();
I am trying to write a query to find the best match. I have an index with the structure below.
public class UserProfileSearch
{
public int Id { get; set; }
public int Sex { get; set; }
public int Age { get; set; }
public int MaritalStatus { get; set; }
public int CountryLivingIn { get; set; }
public double Height { get; set; }
public double BodyWeight { get; set; }
...
}
When I start my search I use different parameters. I get the search parameters as an object which has the structure below.
public class UserPreference
{
public int Id { get; set; }
public int FromAge { get; set; }
public int ToAge { get; set; }
public int FromHeight { get; set; }
public int ToHeight { get; set; }
public string MartialStatus { get; set; } // This will have id in comma separated form: 11,23,24..
public string CountriesLivingIn { get; set; } // This will also have id in comma separated form: 11,23,24..
public string Sexes { get; set; }
...
}
I am trying to achieve like below.
QueryContainer qCs = null;
userPartnerPreference.CountriesLivingIn.Split(",").ToList().ForEach(id =>
{
qCs |= new TermQuery { Field = "countryLivingIn ", Value = int.Parse(id) };
});
QueryContainer qSs = null;
userPartnerPreference.MartialStatus.Split(",").ToList().ForEach(id =>
{
qSs &= new TermQuery { Field = "maritalStatus", Value = int.Parse(id) };
});
var searchResults = await _elasticClient.SearchAsync<UserProfileSearch>(s => s
.Query(q => q
.Bool(b => b
.Must(qSs)
.Should(
bs => bs.Range(r => r.Field(f => f.Age).GreaterThanOrEquals(userPartnerPreference.FromAge).LessThan(userPartnerPreference.ToAge)),
bs => bs.Range(r => r.Field(f => f.Height).GreaterThanOrEquals(userPartnerPreference.FromHeight).LessThanOrEquals(userPartnerPreference.ToHeight)),
bs => bs.Bool(bsb=>bsb.Should(qCs))
)
)
)
);
I basically want to find the best match result based on the parameters passed ordered by highest number of fields matched. I'm new to elastic search so is this the way to do it?
Note: I have other fields that I need to match. There are around 15 field, which I am planning to have inside should like age and height.
I need to populate a Product object which contains two collections.
The current code works fine and populates the Product.GraphicItems collection, but I also need to populate the Product.InfoItems collection, but I can't figure out the syntax for multiple collections.
Current select:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
Product.cs:
[Table("ShopProduct")]
public class Product : BaseShop
{
public bool Active { get; set; } = true;
public int CategoryId { get; set; }
public int CultureId { get; set; } = -1;
public List<ProductInfo> InfoItems { get; set; } = new List<ProductInfo>();
public List<ProductGraphic> GraphicItems { get; set; } = new List<ProductGraphic>();
}
ProductInfo.cs:
[Table("ShopProductInfo")]
public class ProductInfo : BaseShop, IInfoItem
{
public int? ProductId { get; set; }
public int CultureId { get; set; }
public Culture Culture { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Solution:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
I am trying to join two of my tables with linq based on an id, so far unseccesfully.
Here is how my models look :
public class WorkRole
{
public int WorkRoleId { get; set; }
public string RoleName { get; set; }
public string RoleDescription { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<WorkRolesUsersDetails> WorkRolesUsersDetails { get; set; }
}
public class WorkRolesUsersDetails
{
public int WRUDId { get; set; }
public int? WorkRoleId { get; set; }
public string UserDetailsId { get; set; }
public virtual WorkRole WorkRole { get; set; }
public virtual UserDetails UserDetails { get; set; }
public DateTime FocusStart { get; set; }
public DateTime FocusEnd { get; set; }
public bool isActive { get; set; }
}
I am trying to get in one view WorkRoleId, RoleName, RoleDescription and CompanyId from the first table and UserDetailsId, FocusStart, FocusEnd and isActive from the second table.
The farthest i got with my ideas was :
var query = db.WorkRoles.Join(db.WorkRolesUsersDetails,x => x.WorkRoleId,y => y.WorkRoleId,(x, y) => new { wr = x, wrud = y });
But sadly, it didn't work. I just don't know enough linq and couldn't get much out of other questions/answers here. Please, help.
Code for joining 2 tables is:
var list = db.WorkRoles.
Join(db.WorkRolesUsersDetails,
o => o.WorkRoleId, od => od.WorkRoleId,
(o, od) => new
{
WorkRoleId= o.WorkRoleId
RoleName= o.RoleName,
RoleDescription= o.RoleDescription,
CompanyId= o.CompanyId,
WRUDId= od.WRUDId,
UserDetailsId= od.UserDetailsId,
FocusStart=od.FocusStart,
FocusEnd=od.FocusEnd
})
If you are using EF may I suggest the Includes statement it works wonders. IF you have a foreign key assigned. It basically gets the other data with it.
static void Main(string[] args)
{
using (var context = new TesterEntities())
{
var peopleOrders = context.tePerson.Include("teOrder").First(p => p.PersonId == 1).teOrder.ToList();
peopleOrders.ForEach(x => Console.WriteLine($"{x.OrderId} {x.Description}"));
}
}
Combining manually without navigation context option.
public class Student
{
public int StudentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<StudentTestScore> Scores { get; set; }
}
public class StudentTestScore
{
public int StudentID { get; set; }
public int Score { get; set; }
}
class Program
{
static void Main(string[] args)
{
var students = new List<Student>
{
new Student { StudentID = 1, FirstName = "Brett", LastName = "X" },
new Student { StudentID = 2, FirstName = "John", LastName = "X" }
};
var grades = new List<StudentTestScore> { new StudentTestScore { StudentID = 1, Score = 98 } };
var combined = students.Join(grades, x => x.StudentID, y => y.StudentID,
(x, y) => new
{
Student = $"{x.FirstName} {x.LastName}",
Grade = y.Score
}).ToList();
combined.ForEach(x => Console.WriteLine($"{x.Student} {x.Grade}"));
Console.ReadLine();
}
I have the following code:
context.Posts
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types
})
).ToList();
This is working but one Post has Many PostLocalized.
I would like to, in my query, pick the PostLocalized which .Culture == culture.
And I need to use its data to create the PostModel. something like:
context.Posts
// PICK the first PostLocalized which .Culture property equals culture
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types,
//Title = PostLocalized.Title,
//Body = PostLocalized.Body
})
).ToList();
How can I do this?
NOTE:
The Post and PostLocalized entities are the following:
public class Post {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public PostTypes Types { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Post
public class PostLocalized {
public Int32 Id { get; set; }
public String Culture { get; set; }
public String Body { get; set; }
public String Title { get; set; }
public virtual Post Post { get; set; }
public virtual ICollection<Pack> Packs { get; set; }
} // PostLocalized
public class Pack {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public DataType Type { get; set; }
public DateTime Updated { get; set; }
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Pack
public class File {
public Int32 Id { get; set; }
public Byte[] Data { get; set; }
public Guid Key { get; set; }
public String Mime { get; set; }
public virtual Pack Pack { get; set; }
} // File
Thank You,
Miguel
This is not exactly beautiful or efficient on its own but it should at least work and the query optimizer will hopefully make it fast.
context.Posts
.SelectMany(post => post.Packs
.SelectMany(pack => pack.Files
.Select(file => new PostModel
{
Id = post.Id,
File = file.Key,
Types = post.Types,
Title = post.PostsLocalized.First(pl => pl.Culture == culture).Title,
Body = post.PostsLocalized.First(pl => pl.Culture == culture).Body
})))
.ToList();