How to get data using select from a nested properties - linq - c#

Here is my data structure:
public class Product
{
// Rest of props
public ICollection<ProductUpdate> ProductUpdate { get; set; }
}
public class ProductUpdate
{
// Rest of props
public Delivery Delivery { get; set; }
}
public class Delivery
{
// Rest of props
public virtual ICollection<DeliveryUsersApprovers> DeliveryUsersApprovers { get; set; }
}
public class DeliveryUsersApprovers
{
// Rest of props
public User User { get; set; }
}
How can I write a linq method syntax query that would select Id, StartDate and Note from ProductUpdate, while it would select for each row ProductUpdate cooresponding User which made update which is contained in DeliveryUsersApprovers class..
I would like to achieve it using .Select() to get only needed columns..
I've tried something like this but that is not working :
var paymentStatusUpdates = await _dbContext.Product.Include(x => x.ProductUpdate)
.Select(x => new SomeCustomClassObjectWithFourProperties
{
// Read data from ProductUpdate somehow and select Id, Date and Note from ProductUpdate and get User from nested property
.Select(y=> new SomeCustomClassObjectWithFourProperties
{
Id = y.Id,
Date=y.StartDate,
Note=y.Note,
User=? // this is User from very nested prop
})
})
.FirstOrDefaultAsync(x => x.Id == productId, cancellationToken); //productId is received in method params
I'm really struggling to get deep into nested prop and reach User for each ProductUpdate so any kind of help would be great !!
Thanks

Use SelectMany to flatten DeliveryUsersApprovers collection, then map every DeliveryUsersApprovers into a Result object that contain its User and ProductUpdate's data.
I tried this out on a dummy IEnumerable, please replace products in my example with your _dbContext.Product.Include(x => x.ProductUpdate) to see if it works.
var products = new List<Product>();
var results = products.Select(product => product.ProductUpdate
.SelectMany(productUpdate => productUpdate.Delivery.DeliveryUsersApprovers
.Select(deliveryUsersApprovers => new Result()
{
Id = productUpdate.Id,
Date = productUpdate.Date,
Note = productUpdate.Note,
User = deliveryUsersApprovers.User
})));

Related

Cannot implicitly convert type 'ProductOrderDto' to 'System.Collections.Generic.List<ProductOrderDto>

public class ProductOrder
{
public int PId { get; set; }
public Product Product { get; set; }
public int OId { get; set; }
public Order Order { get; set; }
}
public class ProductOrderDto
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Order> Orders { get; set; }
}
public class EfProductOrderDal : EfEntityRepositoryBase<ProductOrderDto, EtnContext>, IProductOrderDal
{
public List<ProductOrderDto> GetProductOrderDetails()
{
using (EtnContext context = new EtnContext())
{
var model = new ProductOrderDto();
model.Products = context.Products.Include(i => i.ProductOrders).ThenInclude(i => i.Order).ToList();
model.Orders = context.Orders.Include(i => i.ProductOrders).ThenInclude(i => i.Product).ToList();
return model;
}
}
}
enter image description here
i would like to perform data insertion and data display of the tables that i relate to many to many, but i am receiving such a mistake
I would like to perform data insertion and data display of the tables
that i relate to many to many, but i am receiving such a mistake.
Well, based on your scenario you might want to either return ProductOrderDto which would containing List of Products and Orders or you would like to return List of ProductOrderDto with collection of Products and Orders. So here are the both way how you could implement those:
Return Collection Of ProductOrderDto:
public List<ProductOrderDto> GetProductOrderDetails()
{
using (EtnContext context = new EtnContext())
{
//As your method signature is List<ProductOrderDto> thus, define your list type
var model = new List<ProductOrderDto>();
//Get each collection of products and Orders
var products = context.Products.Include(i => i.ProductOrders).ThenInclude(i => i.Order).ToList();
var orders = context.Orders.Include(i => i.ProductOrders).ThenInclude(i => i.Product).ToList();
//Bind products and Orders Into List
model.Add(products);
model.Add(orders);
// Now You can return your list
return model;
}
}
Explanation: As you have defined your method return type List so you eventually require to return of type list as well. But you have defined single instance as new ProductOrderDto(); Thus, your return type doesn't match. Therefore, if you would like to assign list of Products and Orders Collection, you ought to create new List() and then finally, you can assign collection into it by model.Add. . You can get more details in our official document here.
Alternative Way:
public List<ProductOrderDto> GetProductOrderDetails()
{
using (EtnContext context = new EtnContext())
{
var model = new List<ProductOrderDto>();
model.Add(context.Products.Include(i => i.ProductOrders).ThenInclude(i => i.Order).ToList());
model.Add(context.Orders.Include(i => i.ProductOrders).ThenInclude(i => i.Product).ToList());
return model;
}
}
Return Single ProductOrderDto:
public ProductOrderDto GetProductOrderDetails()
{
using (EtnContext context = new EtnContext())
{
var model = new ProductOrderDto();
model.Products = context.Products.Include(i => i.ProductOrders).ThenInclude(i => i.Order).ToList();
model.Orders = context.Orders.Include(i => i.ProductOrders).ThenInclude(i => i.Product).ToList();
return model;
}
}
Note: As your ProductOrderDto has the type of IEnumerable and IEnumerable Orders so when you would return ProductOrderDto itself it would be containing the both product and Order collection so that you can directly assign list to your ProductOrderDto object which is Products and Orders.
If you would like to invest few more time on above concept you could have a look on official document here.

NHibernate QueryOver C#, selecting a specific property of a joined Entity to List field within a Dto

I was wondering how to do the following in QueryOver (NHibernate 4.0.4)
Say I have this set of classes
class Student
{
public virtual long Id { get; set; }
public virtual IList<Exam> Exams { get; set; }
}
class Exam
{
public virtual string Subject { get; set; }
public virtual int Score { get; set; }
//Many more unneeded properties
}
class StudentDto
{
public long Id { get; set; }
public IList<string> Subjects { get; set; }
public IList<int> Scores { get; set; }
}
How would I go about getting all students along with their Subjects and Scores without fetching the whole entities?
The functionality I am going for is:
foreach(var exam in student.Exams)
{
dto.Subjects.Add(exam.Subject);
dto.Scores.Add(exam.Score);
}
But hopefully using some of NHibernate's functionality like SelectList
Basically what I am trying to do is something along these lines:
StudentDto dto = null;
Student student = null;
Exam exam = null;
QueryOver<Student>(() => student)
.SelectList(list => list
.Select(st => st.Exams.Select(x => x.Subject)).WithAlias(() => dto.Subjects)
.Select(st => st.Exams.Select(x => x.Score)).WithAlias(() => dto.Scores)
)
I've tried the above, I get an exception that the object is out of scope
QueryOver<Student>(() => student)
.SelectList(list => list
.Select(() => student.Exams.Select(x => x.Subject)).WithAlias(() => dto.Subjects)
.Select(() => student.Exams.Select(x => x.Score)).WithAlias(() => dto.Scores)
)
I've tried this, throws a null reference exception, I've also tried the above with a ternary operator to check for whether or not the lists are null and pass an empty list if so, didn't work
EDIT
I would like to return a list of StudentDtos with every one of them containing a list of the respective student's Scores and Subjects.
So a structure somewhat resembling this
StudentDto
-> long ID
-> List<string> subjects -> Math
-> Physics
-> etc..
-> List<int> scores -> 100
-> 94
-> etc..
That's what JoinAlias is for. If the goal is to get students by some filter then this might look like this
Exam exam = null;
var data = session.QueryOver<Student>()
//.Where( whatever)
.JoinAlias(s => s.Exams, () => exam)
.Select(s => s.Id, s => exam.Subject, s => exam.Score)
.OrderBy(s => s.Id).Asc
.List<object[]>();
var studentDtos = new List<StudentDto>();
foreach (var item in data)
{
var id = (long)item[0];
StudentDto current = studentDtos.LastOrDefault();
if (studentDtos.Count == 0 || current.Id != id)
{
studentDtos.Add(current = new StudentDto { Id = id });
}
current.Subjects.Add((string)item[1]);
current.Scores.Add((int)item[2]);
}

How can I read just one field in MongoDB C#?

Im trying to read just one field from the MongoDB with a given ID, but its giving me all the object... How can I do it ? What should I do? I tried with this repository but its returning me all the object
Here is my class:
public class Hall
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public List<string> Surveys { get; set; }
}
And here is my repository with the connection to Mongo:
public Task<List<Hall>> ReadSurveysHall(string hallId)
{
var filter = Builders<Hall>.Filter.(x => x.Id, hallId);
return _mongoManager.Find(filter, MongoConstants.HallCollection);
}
I just want it to give me just the list of Survey's string.
Thank you in advance.
In order to return only the list of surveys, you can change your method like this:
public async Task<List<string>> ReadSurveysHall(string hallId)
{
var filter = Builders<Hall>.Filter.(x => x.Id, hallId);
var halls = await _mongoManager.Find(filter, MongoConstants.HallCollection);
var hall = halls.FirstOrDefault();
if (hall == null)
return new List<string>();
return hall.Surveys;
}
You can further optimize this by using a projection so that MongoDB only delivers the data that you are interested in, e.g.:
var projection = Builders<Hall>.Projection.Exclude(x => x.Id).Include(x => x.Surveys);
var options = new FindOptions<Hall, Hall>()
{
Projection = projection,
};
How you use that projection depends on the capabilities of _mongoManager. Take a look at the Find methods and check whether any of these accepts a projection or some kind of FindOptions as a parameter.

getting individual fields from a linq query which is run from a function

I have a function that return a linq result :
private IEnumerable<object> prepareData()
{
var data = from res in Globals.ds.Tables[0].AsEnumerable()
.GroupBy(x => new
{
art = x.Field<string>("artiste"),
alb = x.Field<string>("album"),
})
.Select(p => new
{
album = p.Key.alb,
artiste = p.Key.art,
count_lab = p.Count(),
lab = p.Select(x => x.Field<string>("label")).First(),
filp = p.Select(x => x.Field<string>("file_path")).First()
})
.OrderBy(x => x.lab)
select res;
return data;
}
The query works well as designed, i can do data = PrepareData(); and get the right results.
My issue is when i want to do a .where on the data.
if i do :
var album = data.Where(x => x.
Then i dont have any option to select a single field (it's the same if i want to do a .Select()).
I tried data.AsEnumerable() before but to no success.
I'm thinking the IEnumerable<object> prepareData() is the culprit, but i have no idea how to fix this (if ever it's the case).
I need help
Thanks in advance
If you want to select a single field you can use: First() or FirstOrDefault(). Difference between this two is:
First() will throw an exception if an element is not found.
FirstOrDefault() will return null if element is not found.
Also if you want to fix the problem with IEnumerable<object> you need to create an DTO class where you can map all items from select.
Something like this:
public class DTOClass
{
public string album { get; set; }
public string artiste { get; set; }
public string count_lab { get; set; }
public string lab { get; set; }
public string filp { get; set; }
}
And then in select you can simply do:
...
Select(p => new DTOClass {
// map the values for DTO class here
}

LINQ GroupBy Aggregation with AutoMapper

Trying to get a query to work, but honestly not sure how (or if it's even possible) to go about it as everything I have tried hasn't worked.
Querying a total of 6 tables: Person, PersonVote, PersonCategory, Category, City, and FirstAdminDivision.
PersonVote is a user review table for people and contains a column called Vote that is a decimal accepting a value from 1-5 (5 being "best"). FirstAdminDivision would be synonymous with US states, like California. Person table has a column called CityId which is the foreign key to City. The other tables I believe are mostly self-explanatory so I won't comment unless needed.
My goal is create a query that returns a list of the "most popular" people which would be based on the average of all votes on the PersonVote table for a particular person. For instance, if a person has 3 votes and all 3 votes are "5" then they would be first in the list...don't really care about secondary ordering at this point...eg...like most votes in a tie would "win".
I have this working without AutoMapper, but I love AM's ability to do projection using the ProjectTo extension method as the code is very clean and readable and would prefer to use that approach if possible but haven't had any luck getting it to work.
Here is what I have that does work....so basically, I am trying to see if this is possible with ProjectTo instead of LINQ's Select method.
List<PersonModel> people = db.People
.GroupBy(x => x.PersonId)
.Select(x => new PersonModel
{
PersonId = x.FirstOrDefault().PersonId,
Name = x.FirstOrDefault().Name,
LocationDisplay = x.FirstOrDefault().City.Name + ", " + x.FirstOrDefault().City.FirstAdminDivision.Name,
AverageVote = x.FirstOrDefault().PersonVotes.Average(y => y.Vote),
Categories = x.FirstOrDefault().PersonCategories.Select(y => new CategoryModel
{
CategoryId = y.CategoryId,
Name = y.Category.Name
}).ToList()
})
.OrderByDescending(x => x.AverageVote)
.ToList();
By looking at your code sample I tried to determine what your models would be in order to setup an example. I only implemented using a few of the properties to show the functionality:
public class People
{
public int PeronId { get; set; }
public string Name { get; set; }
public City City { get; set; }
public IList<PersonVotes> PersonVoes { get; set; }
}
public class PersonVotes
{
public int Vote { get; set; }
}
public class City
{
public string Name { get; set; }
}
public class FirstAdminDivision
{
public string Name { get; set; }
}
public class PersonModel
{
public int PersonId { get; set; }
public string Name { get; set; }
public string LocationDisplay { get; set; }
public double AverageVote { get; set; }
}
To use the ProjectTo extension I then initialize AM through the static API:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<IEnumerable<People>, PersonModel>()
.ForMember(
model => model.LocationDisplay,
conf => conf.MapFrom(p => p.FirstOrDefault().City.Name))
.ForMember(
model => model.AverageVote,
conf => conf.MapFrom(p => p.FirstOrDefault().PersonVoes.Average(votes => votes.Vote)));
});
So given the following object:
var people = new List<People>()
{
new People
{
PeronId = 1,
City = new City
{
Name = "XXXX"
},
PersonVoes = new List<PersonVotes>
{
new PersonVotes
{
Vote = 4
},
new PersonVotes
{
Vote = 3
}
}
}
};
I would then a have query:
var result = people
.GroupBy(p => p.PeronId)
.Select(peoples => peoples)
.AsQueryable()
.ProjectTo<PersonModel>();
I'm just using in memory objects so that is why I convert to IQueryable to use the ProjectTo extension method in AM.
I'm hoping this was what you're looking for. Cheers,
UPDATED FOR LINQ TO ENTITIES QUERY:
var result = db.People
.GroupBy(p => p.PersonId)
.ProjectTo<PersonModel>(base.ConfigProvider) // AM requires me to pass Mapping Provider here.
.OrderByDescending(x => x.AverageVote)
.ToList();

Categories

Resources