I have two model classes
public class GetProductNameRequest
{
public DateTime ExpiryDate { get; set; }
public bool IsActive { get; set; }
}
public class GetProductNameResponse
{
public List<string> ProductName { get; set; }
}
linq query:
public async Task<List<ProductSKU>> GetProductNames(GetProductNameRequest request)
{
var res = DbSet.Include(t => t.Product).Where(t => t.EndDate >= request.ExpiryDate && t.IsActive == request.IsActive).GroupBy(x => x.Product);
Contracts.Models.ProductNameResponse product = new Contracts.Models.ProductNameResponse();
foreach (var item in res)
{
product.ProductName = item.Key.ProductName;
}
}
So i'm unble get list of Product Names based on Id's plz let me know the solution.
productsku table:
SkuId ProductId Sku MRP CreatedOn UpdatedOn StartDate EndDate IsActive RowVersion
You have a number of problems in your code. Two most obvious reasons why you don't get anything in your product variable is that it is initialized inside the loop, and nothing gets added to it. The code should be something like
Contracts.Models.ProductNameResponse product = new Contracts.Models.ProductNameResponse();
foreach (var item in res)
{
product.ProductName.Add(item.Key.ProductName);
}
I also think your LINQ statement will still throw an error about one cursor not close while another one is open. Search for IQueryable vs IEnumerable issue. But you don't list that as a problem; so maybe in your data source it is fine.
Related
While using LinQ, retrieving data from foreign key table data are available. But when I try to 'Add' into my ViewModel this warnings shows. Warningsare difference from each other. such as,
Cannot implicitly convert type 'System.Collection.Generic.IEnumerable<string>' to 'string'
and
Cannot implicitly convert type 'System.Collection.Generic.IEnumerable<System.Collection.Generic.IEnumerable.IEnumerable<string>' to 'string'.
I tried casting to ToString() but it was worthless, no errors shows but data was replaced with system messages. I also tried to LinQ Join in students but I was unable to show Skills Comma Separated that way.
Here is my code:
public ActionResult GetStudentsInfo ()
{
var students = (from stud in db.Students
group stud by stud.StudentId into sg
select new
{
studentName=sg.Select(s=>s.StudentName).FirstOrDefault(),
coutryName=sg.Select(c=>c.Country.CountryName),
cityName=sg.Select(ct=>ct.Country.Cities.Select(x=>x.CityName)),
skillName=sg.Select(sk=>sk.StudentSkills.Select(s=>s.Skill.SkillName)),
resumeName=sg.Select(r=>r.Resumes.Select(m=>m.ResumeName)),
dob=sg.Select(d=>d.DateOfBirth)
}).ToList();
List<StudentListVM> studentLists=new List<StudentListVM>();
foreach (var item in students)
{
studentLists.Add(new StudentListVM
{
studentName = item.studentName,
country = item.coutryName, //warnings here
city = item.cityName, //warnings here
skills = string.Join(",", item.skillName),
resume = item.resumeName, //warnings here
dateOfBirth = item.dob //warnings here
});
}
return View(studentLists);
}
```
StudentListVM class
public class StudentListVM
{
public string studentName { get; set; }
public string country { get; set; }
public string city { get; set; }
public string skills { get; set; }
public string resume { get; set; }
public DateTime dateOfBirth { get; set; }
}
```
I tried this before
var students = (from stud in db.Students
join con in db.Countries on stud.CountryId equals con.CountryId
join ct in db.Cities on stud.CityId equals ct.CityId
join rsm in db.Resumes on stud.ResumeID equals rsm.ResumeId
join stsk in db.StudentSkills on stud.StudentId equals stsk.StudentId
//group stud by stud.StudentId into sg
select new StudentListVM()
{
studentName = stud.StudentName,
countries = con.CountryName,
cities = ct.CityName,
skills=stsk.Skill.SkillName,
resumes = rsm.ResumeName,
dateOfBirth = stud.DateOfBirth,
}).ToList();
```
StudentSkill class:
public partial class StudentSkill
{
public int StudentSkillsId { get; set; }
public int StudentId { get; set; }
public int SkillId { get; set; }
public virtual Student Student { get; set; }
public virtual Skill Skill { get; set; }
}
```
This returns All fine except The Skills in a comma separated list. All I need to show my multiple skills that are checked multiply and added to the database in a separated table name StudentSkills. Is there any good solution to do it?
You are trying to assign a group of strings, specifically the various IEnumerable<string> collections, into a single string due to your Select() calls.
For example, this line is clearly selecting more than one resume name.
resumeName=sg.Select(r=>r.Resumes.Select(m=>m.ResumeName))
If you don't care and expect them to be all the same value you could just grab the first one:
resume = item.resumeName.FirstOrDefault()
Or flatten the collection some other way.
That said, there's something off with the design when you grab a collection and try to assign it to a single item.
Try to change this line:
skills = string.Join(",", item.skillName),
With this:
skills = string.Join(",", item.skillName.ToArray()),
The problem you have is not in your code. The problem is how you think you are solving it. Think it right, the solution will be right. #Zer0 already mentioned that but you probably need more explanation. I will try to explain with some assumption that you might be wanting to do --
1: If Student can have multiple country, city, skill and resumes, then the StudentVM class you have is most certainly wrong. By definition it only supports one city, country, skill, etc. Modify it to support multiples -
public class StudentListVM
{
public string studentName { get; set; }
public List<string> countries { get; set; }
public List<string> cities { get; set; }
public string skills { get; set; }
public List<string> resume { get; set; }
//does not make sense to have a list, a person has only one DOB
public DateTime dateOfBirth { get; set; }
}
then the code you have will work -
public ActionResult GetStudentsInfo ()
{
var students = (from stud in db.Students
group stud by stud.StudentId into sg
select new
{
studentName=sg.Select(s=>s.StudentName).FirstOrDefault(),
coutryName=sg.Select(c=>c.Country.CountryName),
cityName=sg.Select(ct=>ct.Country.Cities.Select(x=>x.CityName)),
skillName=sg.Select(sk=>sk.StudentSkills.Select(s=>s.Skill.SkillName)),
resumeName=sg.Select(r=>r.Resumes.Select(m=>m.ResumeName)),
dob=sg.Select(d=>d.DateOfBirth).FirstOrDefault()
}).ToList();
List<StudentListVM> studentLists=new List<StudentListVM>();
foreach (var item in students)
{
studentLists.Add(new StudentListVM
{
studentName = item.studentName,
countries = item.coutryName.ToList(), //should work, as these are lists
cities = item.cityName.ToList(), //should work, as these are lists
skills = string.Join(",", item.skillName),
resume = item.resumeName.ToList(), //should work, as these are lists
dateOfBirth = item.dob //does not make sense to have a list, a person has only one DOB
});
}
return View(studentLists);
}
2: Once the class is okay, you could shorten the code. You don't need a second block to create a typed list, you can do it directly -
public ActionResult GetStudentsInfo ()
{
var students = (from stud in db.Students
group stud by stud.StudentId into sg
select new StudentListVM
{
studentName=sg.Select(s=>s.StudentName).FirstOrDefault(),
countries=sg.Select(c=>c.Country.CountryName).ToList(),
cities=sg.SelectMany(ct=>ct.Country.Cities.Select(x=>x.CityName)).ToList(),
skills=string.Join(",", sg.Select(sk=>sk.StudentSkills.Select(s=>s.Skill.SkillName))),
resume=sg.SelectMany(r=>r.Resumes.Select(m=>m.ResumeName)).ToList(),
//does not make sense to have a list, a person has only one DOB
dob=sg.Select(d=>d.DateOfBirth).FirstOrDefault()
}).ToList();
return View(students);
}
3: If the above does not make sense, then the idea is not right. Think of what you are trying to achieve and update the question. May be then people will be able to help.
You have said -
Actually I can do them separately in different ways but I can't do it in a single code
So what are those ways? Mentioning them will probably give an idea of what you are trying to achieve. The details on the question is not enough to give you solution. It does not say what you are trying to do.
Is it possible to rewrite this code to LINQ? I'm new to linq and it seems difficult to understand.
foreach (Employee employee in EmployeeList)
{
Earnings earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account);
if (!EarningsList.Contains(earnings))
{
EarningsList.Add(earnings);
}
foreach (DaysData item in ProductList)
{
foreach (Product product in item.Products)
{
if (product.EmployeeName == employee.Name && product.EmployeeLastName == employee.LastName)
{
double money = product.Count * product.Price;
earnings.AddMoney(money);
}
}
}
}
The first part isn't so easy to convert because of the conditional EarningsList.Add()
But You can rewrite the last 2 rather easily.
Assuming that AddMoney() does just what it says, you can use Sum(). Otherwise, omit the Sum() and run a separate foreach on the list of amounts. That would make it a lot less Linq.
var amount = ProductList
.SelectMany(item => item.Products)
.Where(product => product.EmployeeName == employee.Name && product.EmployeeLastName == employee.LastName)
.Sum(product => product.Count * product.Price)
;
earnings.AddMoney(amount);
Not knowing exactly what the .AddMoney method does you could do this:
var query =
from employee in EmployeeList
let earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account)
from item in ProductList
from product in item.Products
where product.EmployeeName == employee.Name
where product.EmployeeLastName == employee.LastName
group product.Count * product.Price by earnings;
List<Earnings> EarningsList =
query
.Aggregate(new List<Earnings>(), (a, x) =>
{
a.Add(x.Key);
foreach (var y in x)
{
x.Key.AddMoney(y);
}
return a;
});
If .AddMoney simply adds the money arithmetically then you could do this:
List<Earnings> EarningsList =
query
.Aggregate(new List<Earnings>(), (a, x) =>
{
a.Add(x.Key);
x.Key.AddMoney(x.Sum());
return a;
});
Just a small note. You're using double to represent money. It's best to use decimal as this will help prevent rounding errors in your calculations.
I think this is what you want if you just used LINQ(no foreach). This should be compatible with IQueryable as well and you really don't want to do foreach on IQueryable.
var newEarnings = from employee in EmployeeList
select new Earnings
{
Name = employee.Name,
LastName = employee.LastName,
Bank = employee.Bank,
Account = employee.Account,
Money = (from daysData in ProductList
from product in daysData.Products
where employee.Name == product.EmployeeName && employee.LastName == product.EmployeeLastName
select product).Sum(p => p.Count * p.Price)
};
EarningsList = EarningsList.Union(newEarnings).ToList();
Now in regards to normalization. My guess is that you made you POCO models like this in order to show it in some sort of a grid. You really should not let your UI dictate what you data models look like. There can be others reasons to do this but they are related to performance and I don't think you need to worry about this just jet. So here is my advice on how to change this.
Add Id property to all the classes. This is always a good idea no
matter what you are doing. This can be a random string or an auto
increment, just to have a unique value so you can play with this
object.
Add reference properties in you classes. Don't copy the values from
Employee to Product and Earnings. Just add a Property of type
Employee and/or add the EmployeeId property
So your POCO should look something like this
public class Employee
{
//It can be a Guid, string or what ever. I am not nesseserly recomending using Guids and you should look into this a bit more
public Guid Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Bank { get; set; }
public string Account { get; set; }
}
public class DaysData
{
public Guid Id { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product
{
public Guid Id { get; set; }
public Guid EmployeeId { get; set; }
public Employee Employee { get; set; }
public double Count { get; set; }
public double Price { get; set; }
}
public class Earnings
{
public Guid Id { get; set; }
public Guid EmployeeId { get; set; }
public Employee Employee { get; set; }
public double Money { get; set; }
}
And the query
var newEarnings = from employee in EmployeeList
select new Earnings
{
Id = Guid.NewGuid(),
EmployeeId = employee.Id,
Employee = employee,
Money = (from daysData in ProductList
from product in daysData.Products
where employee.Id == product.EmployeeId
select product).Sum(p => p.Count * p.Price)
};
Once you try to implement data persistence this will help you a lot no matter what you use EF, Mongo, Elsatic, AzureTables or anything else even a simple saving to files.
I am just sending this data to reporting Engine(SSRS) in Asp.net MVC5.
Everything is fine, but this query is taking a lot of time since I have to loop through ListProducts (I guess ListProducts is the size of the database matches).
I am just looking for a way to optimize this query.
I have tried any and contains (as seen below), but they do not seem to work in single table.
context.Products.Where(w => w.ProductDetail.Any(a => a.startDate >= startDate
&& a.endDate <= endDate))
I got this from here
2)I tried this as well
context.Products.Where(w => ListProducts.Any(x =>w.Contains(x)))
but this also does not work and generates a compile time error that
System.Guid does not contains definition of 'Contains'
Is there any other way, or i am doing it the only correct way?
foreach (var item in ListProducts)
{
List.AddRange(_context.Products.Where(w => w.ProductId== item).Select(q => new ProductVM
{
Name = q.Name,
Quantity = q.Quantity,
}).ToList().Select(item=> new ProductVM
{
Name = item.Name,
Quantity = item.Quantity,
}).ToList());
}
public class Product
{
public Nullable<System.Guid> ProductId { get; set; }
public string Name { get; set;}
public decimal Quantity { get; set; }
}
Ok, can you do:
var thelist = _context.Products.Where(n => ListProducts.Contains(n.ProductId)).Select(n => new ProductVM
{
Name = n.Name,
Quantity = n.Quantity
}).ToList();
Need a help with RavenDB.
In my web page I want to have such list:
item1 category1
item2 category2
...
and another one:
category1, number of items
category2, number of items
...
My data structures:
public class Item
{
public string Id { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
}
public class Category
{
public string Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
Index for the first list:
public class Item_WithCategory : AbstractIndexCreationTask<Item>
{
public class Result
{
public string Name { get; set; }
public string CategoryName { get; set; }
}
public Item_WithCategory()
{
Map = items => from item in items
select new
{
Name = item.Name,
CategoryName = LoadDocument<Category>(item.CategoryId).Name
};
}
}
Is this data structure suitable for my case, or will it be better to have Category instead of CategoryId in item structure?
Should I use my index or is there a better solution to take category name?
If my index is good, how to write a correct query? My current try:
Item_WithCategory.Result[] all;
using (var session = DocumentStoreHolder.Store.OpenSession())
{
all = session.Query<Item_WithCategory.Result, Item_WithCategory>().ToArray();
}
but it throws exception stating that return type is item, not result. How to fix it?
You have a couple of options here. You could store both the CategoryId and the CategoryName on the Item entity. This will of course lead to duplicated data (if you still need to store the Category entity), but "storage is cheap" is a popular term these days.The downside of this is that you need to update each Item document of a given category if the category name changes to keep things consistent. A benefit is that you need to do less work to get your desired result.
If you store Category Name on the item as well you don't need a special index to handle the first list, just query on the Items and return what you need. For the second list you need to create a Map/Reduce index on the Item entity that groups on the category.
However, if you need to use the data structure you've given, there are a couple of ways of solving this. First, it's really not recommended to use a LoadDocument inside of an index definition, especially not in a select statement. This might affect indexing performance in a negative way.
Instead, just index the properties you need to query on (or use an auto index) and then use a Result Transformer to fetch information from related documents:
public class ItemCategoryTransformer : AbstractTransformerCreationTask<Item>
{
public ItemCategoryTransformer()
{
TransformResults = results => from item in results
let category = LoadDocument<Category>(item.CategoryId)
select new ItemCategoryViewModel
{
Name = item.Name,
CategoryName = category.Name
};
}
}
public class ItemCategoryViewModel
{
public string Name { get; set; }
public string CategoryName { get; set; }
}
You can use this Transformer with a Query on the Item entity:
using (var session = documentStore.OpenSession())
{
var items = session.Query<Item>()
.TransformWith<ItemCategoryTransformer, ItemCategoryViewModel>()
.ToList();
}
As for the second list, still using your data structure, you have to use a couple of things. First, a Map/Reduce index over the Items, grouped by CategoryId:
public class Category_Items_Count : AbstractIndexCreationTask<Item, Category_Items_Count.Result>
{
public class Result
{
public string CategoryId { get; set; }
public int Count { get; set; }
}
public Category_Items_Count()
{
Map = items => from item in items
select new Result
{
CategoryId = item.CategoryId,
Count = 1
};
Reduce = results => from result in results
group result by result.CategoryId
into c
select new Result
{
CategoryId = c.Key,
Count = c.Sum(x => x.Count)
};
}
}
But as you only have the CategoryId on the Item entity, you have to use a similar transformer as in the first list:
public class CategoryItemsCountTransformer : AbstractTransformerCreationTask<Category_Items_Count.Result>
{
public CategoryItemsCountTransformer()
{
TransformResults = results => from result in results
let category = LoadDocument<Category>(result.CategoryId)
select new CategoryItemsCountViewModel
{
CategoryName = category.Name,
NumberOfItems = result.Count
};
}
}
public class CategoryItemsCountViewModel
{
public string CategoryName { get; set; }
public int NumberOfItems { get; set; }
}
And lastly, query for it like this:
using (var session = documentStore.OpenSession())
{
var items = session.Query<Category_Items_Count.Result, Category_Items_Count>()
.TransformWith<CategoryItemsCountTransformer, CategoryItemsCountViewModel>()
.ToList();
}
As you can see, there are quite a difference in work needed depending on what data structure you're using. If you stored the Category Name on the Item entity directly you wouldn't need any Result Transformers to achieve the results you're after (you would only need a Map/Reduce index).
However, Result Transformers are executed server side and are only executed on request, instead of using LoadDocument inside of an index which is executed every time indexing occurs. Also, and maybe why LoadDocuments inside of index definitions isn't recommended, every change to a document that's referenced with a LoadDocument in an index will cause that index to have to be rewritten. This might lead to a lot of work for the index engine.
Lastly, to answer your last question about why you get an exception when querying: As the actual return type of your index is the document that that's being indexed (in this case Item). To use something else you need to project your result to something else. This can be done by using ".As()" in the query:
Item_WithCategory.Result[] all;
using (var session = DocumentStoreHolder.Store.OpenSession())
{
all = session.Query<Item_WithCategory.Result, Item_WithCategory>()
.As<Item_WithCategory.Result>()
.ToArray();
}
Long post, but hope it helps!
I recently started to use LINQ to SQL and i have a minor complex query i need help with.
I've got a table in my database called MovieComment, with the following columns:
CommentID
UserID
MovieID
Comment
Timestamp
So, what i wanna do is to group the comments on MovieID and save them into my object called Movie, where the MovieID is being saved in the MovieID post, and the Linq object is saved inside the ObservableCollection inside the Movie object.
public class Movie
{
#region Member Variables
public int MovieID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Uri Poster { get; set; }
public double Rating { get; set; }
public DateTime Timestamp { get; set; }
public ObservableCollection<MovieComment> Comments { get; set; } // Linq object: MovieComment
#endregion // Member Variables
}
I've come up with the following linq query where i get the MovieID, but i dont really know how i should proceed to get a hold of all the other data
public ObservableCollection<Movie> LoadMovieID(int _userID, int _limit)
{
ObservableCollection<Movie> movies = new ObservableCollection<Movie>();
var query = (from mc in db.MovieComment
where mc.UserID == _userID
orderby mc.Timestamp descending
group mc by mc.MovieID into movie
select new
{
MovieID = movie.Key,
}).Take(_limit);
foreach (var row in query)
{
Movie movie = new Movie();
movie.MovieID = row.MovieID;
// I want to get the following:
// movie.MovieComment = MovieComment-objects with the MovieID == row.MovieID
movies.Add(movie);
}
return movies;
}
Is this even possible in a single query? Thankful for all the help i can get
Well, you could try this:
var query = (from mc in db.MovieComment
where mc.UserID == _userID
orderby mc.Timestamp descending
group mc by mc.MovieID).Take(_limit);
That will give you a IGrouping<MovieComment, string> (or whatever your types are) which should let you get at all the comments without any extra work. It's certainly okay in terms of LINQ itself, but whether it will do what you want within LINQ to SQL, I'm not sure.