Linq create an array of Dto objects, with an array in it - c#

I have a rich NHibernate dbo object that I need to query to extract a keyword and related array of country codes.
To explain:
I have 3 related DBO objects returned from a database, that have the following structure:
class DboCountry{
public int CountryId {get;set;}
public string CountryCode {get;set;}
}
class DboPage{
public int Id {get;set;}
public string Keyword {get;set;}
}
class DboPageCountry{
public int Id {get;set;}
public DboThemePage DboThemePage {get;set;}
public DboCountry DboCountry {get;set;}
}
As such when querying the database I get a List of DboPageCountries that contain multiple repeating DboPage.Keyword that are associated to various DboCountry.CountryCode
What I need to do is take the List of DboPageCountries and create an array of DtoKeywordCountryCode with the following structure:
class DtoKeywordCountryCode{
public string Keyword {get; set;}
public string[] CountryCodes {get; set;}
}
So far, using Linq, I've either been able to group the keywords, but not get the associated country codes, or get keywords and country codes, but not associated as a unique Keyword against an array of applicable CountryCodes.
Any pointers in the right direction much appreciated.

Try this -
var items = new List<DboPageCountry>(); //list of DboPageCountries
var items2 = from x in items.GroupBy(x => x.DboThemePage)
select new DtoKeywordCountryCode { Keyword = x.First().DboThemePage.Keyword, CountryCodes = x.Select(c => c.DboCountry.CountryCode).ToArray() };

DtoThemePage[] dtoThemePageArrayFull = (from tpc in dboThemePageCountryList group tpc by tpc.DboThemePage.Keyword into k
select new DtoThemePage
{
Keyword = k.Key,
CountryCodes = k.Select(c => c.DboCountry.CountryCode).ToArray<string>()
}).ToArray<DtoThemePage>();

Related

Get prev, current, next on MongoDB c#

I have a Collection Students which Stores registered students data in MongoDB. It has StudentName, SaveDate etc. fields. I am displaying the details to my website from my .net framework WebAPI.
What I want to do is Get StudentDetails with prev next student ID's by the order of SaveDate.
My current query gets Student details by student id from below query,
var collection = StudentDB.Collection<Student>("StudyMaterials");
var curfilter = Builders<Student>.Filter.Eq(x => x.studentID, studentID);
studentDetails= collection.Find(curfilter).FirstOrDefault();
This is my Student.cs class,
public class Student
{
[BsonRepresentation(BsonType.String)]
public Guid StudentID {get; set;}
public string StudentName {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid NextStudentID {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid PrevStudentID {get; set;}
}
I want to use aggregation but don't know how to sort by multiple sort definitions on the aggregation framework.
First of all, I don't think you need to keep the students like that. I suggest the following:
public class Student
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public DateTime SaveDate { get; set; }
}
You need to make sure that the documents are sorted by SaveDate. For instance, when inserting new students into the database you can do it like this:
var student = new Student
{
Name = "Sara",
SaveDate = DateTime.UtcNow
};
context.Students.InsertOne(student);
Now, you can get the documents with Skip and Limit:
var skip = 0;
var limit = 1;
var context = new MongoContext();
var filter = Builders<Student>.Filter.Empty;
var result = context.Students.Find(filter).Skip(skip).Limit(limit).FirstOrDefault();
Increment skip to get next and so on.

Select&Distinct into new object with DLinq

Suppose that I have list of following objects
public class OrderInfo
{
public string OrderNo {get; set;}
public DateTime OrderDate {get; set;}
public decimal OrderAmount {get; set;}
public decimal OrderPrice {get; set;}
}
as
List<OrderInfo> data = new List<OrderInfo>();
and this list has necessary number of items in it.
I need to select distinct OrderNo, OrderDate pairs from this List. In Linq I can write a query as:
var q = (from x in data
group x by new { x.OrderNo, x.OrderDate }
into grp
select new
{
grp.Key.OrderNo,
grp.Key.OrderDate
}).Distinct();
The thing is that I am writing a generic method so the object type and properties are unknown. So I have to write this with DLinq.
If I have a list of List<T> and a string[] { "OrderNo", "OrderDate" } how can I apply this example with DLinq at the run-time?
It was my fault. I was Googling for "DLinq" but not "Dynamic LinQ". Actually the answer was quite simple :
var q = data.Select("new (OrderNo, OrderDate)").Distinct();

C# driver 2.0 Mongodb UpdateOneAsync

public class Student
{
public long StudentId {get; set;}
public string Fname {get; set;}
public string Lname {get; set;}
public List<ObjectId> CoursesList {get; set;}
public int IQ {get;set;}
}
public class Courses
{
[BsonId]
public ObjectId Id { get; set; }
public string CourseNumber{get; set;}
public string CourseName{get; set;}
}
How do I add/append a courser Id to Course list(which may be null for the first time) of a Student object
PS: I know how to set a field using the below command. I am hoping it is on similar lines for the above problem
await StudentCollection.UpdateOneAsync(a => a.StudentId == studentId, Builders<Student>.Update.Set( a => a.IQ,90));
As you've already discovered, the C# code to use $addToSet is:
var filter = Builders<Student>.Filter.Eq(s => s.StudentId, studentId);
var update = Builders<Student>.Update.AddToSet(s => s.CoursesList, courseId);
var result = await collection.UpdateOneAsync(filter, update);
However, $addToSet is not going to work if the CourseList member has been stored in the collection as a null. The server requires that the existing value for $addToSet be an array (it can be an empty array).
The easiest solution is to just store an empty list for CoursesList instead of a null when there are no courses.
This is working for me. When you define "List" like this, it will be empty and works with AddToSet/Push methods.
public List<ObjectId> CoursesList = new List<ObjectId>();
The only case that you have to pay attention is when the array CourseList is null, in this case you have to use the code below (see also here):
var newListOfObject = new List<ObjectId>(){...}
await StudentCollection.UpdateOneAsync(a => a.StudentId == studentId, Builders<Student>.Update.Set( a => a.CoursesList, newListOfObject));
Otherwise you can use AddToSet or Push like explain in the other answers.

Any of the properties equals any of a list of objects

I have a problem in Entity-Framework, using Code-First, that I couldn't solve.
Having entities of the type
public class Product {
public int ID {get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category {
public int ID {get; set;}
public virtual ICollection<Product> Products { get; set; }
// rest omitted
}
in my database, i try to get all Products that have at least one Category from a list of given Categories. I need an Expression as this expression is combined with other expressions later.
Ie. i tried:
var searchFor = new List<Category>{...};
var expression = product => product.Categories.Any(cat => searchFor.Contains(cat))
Executing this later against a DbContext
context.Products.Where(expression).ToList();
creates an exception stating mainly that This context supports primitive types only.
Changing it to
var expression = product => product.Categories.Any(
cat => searchFor.Any(d => d.ID == cat.ID));
to get rid of the object comparison didn't help. I'm stuck. How can I manage that?
You should get rid of List<Category>, replacing it with a list of IDs, like this:
// I'm assuming that ID is of type long; please fix as necessary
var searchFor = new List<long>{...};
var expression = product =>
product.Categories.Any(cat => searchFor.Contains(cat.ID))
If you've already got a list of categories, you can build a list of IDs outside the query:
var searchForIds = searchFor.Select(x => x.ID).ToList();
var query = context.Products
.Where(product => product.Categories
.Any(cat => searchForIds.Contains(cat.ID)));
I don't know that that will work, but it might. (Apologies for the indentation... it's just to avoid scrolling.)

Entity Framework 4: How to code projection to a class type?

If I have a class like the following:
public class Customer {
public int id {get;set;}
public string name {get;set;}
public string line1 {get;set;}
public string line2 {get;set;}
public string line3 {get;set;}
public string line4 {get;set;}
}
And I only want to select the ID and Name values, leaving the rest null.
var myCustomerList = DC.Customer.Select(
p => new Customer { id = p.id, name = p.name });
I get the following error:
The entity or complex type 'MyModel.Customer' cannot
be constructed in a LINQ to Entities query.
How else would you do it? Am I required to specify all the Class's fields?
Try this:
var myCustomerList = from c in DC.Customer
select new { id = p.id, name = p.name };
The above will create an Anonymous Type.
Practical Application:
"var myCustomerList" <-- Anonymous Type.
An anonymous type with two properties "id" and "name". Also, "var" lets you create an Implicitly typed local variable. This means:
a) You didn't have to declare/write a class structure to hold a type with only those two properties;
b) You don't have to maintain that either - you can change the structure of the above query, and "it just works".
Another option is to create a CustomerInfo type:
public class CustomerInfo
{
public int Id { get; set;}
public string Name { get; set; }
}
You can't map two types directly to the same table in EF but you can easily create a view for your info type and then map to that:
CREATE VIEW vwCustomerInfo AS SELECT Id, Name FROM Customer
You then map your CustomerInfo type to your view:
public class CustomerInfoMap : EntityConfiguration<CustomerInfo>
{
public CustomerInfoMap()
{
.ToTable("vwCustomerInfo");
}
}
A side-effect of this is that EF will only retrieve the columns in the view when querying your database. When retrieving a CustomerInfo by id you'll get SQL like this:
SELECT Id, Name FROM vwCustomers WHERE id = 1
In addition, as long as your view is updatable you can update your CustomerInfo type from EF and the underlying table will be updated.

Categories