I have two tables, for Users and Songs. I need to JOIN them both on the UserID column and return a list to the view. I tried writing an anonymous type but I'm getting an error about:
'Only parameterless constructors and initializers are supported in LINQ to Entities.'
How can I JOIN and return to list?
public class User
{
[Key]
public virtual Guid UserId { get; set; }
[Required]
public virtual String Username { get; set; }
[Required]
public virtual String Email { get; set; }
[Required, DataType(DataType.Password)]
public virtual String Password { get; set; }
}
public class Song
{
public int SongId { get; set; }
public virtual Guid UserId { get; set; }
public string SongTitle { get; set; }
}
I tried implementing a method like this one here:
What is the return type of my linq query?
The query I ended up with was:
var query = from u in db.Users
join s in db.Songs
on u.UserId equals s.UserId
select new HomeSongList(s.SongId, s.UserId, u.Username);
return View(query.ToList());
You can't use parameterized constructors but you can use initializers, if you change your select to the following it should work.
select new HomeSongList{SongId=s.SongId, UserId=s.UserId, Username=u.Username};
Note that this will require that HomeSongList has a parameterless constructor and writable properties.
Well the error message is rather clear, no ?
You try to create a new HomeSongList in an linq2entity query (which will be translated in sql : not possible)
With the "User property" correction, you should make
var query = db.Songs.Select(s => new{
songId = s.SongId,
userId = s.User.UserId,
userName = s.User.Username})
.ToList()
.Select(x => new HomeSongList(x.songId, x.userId, x.userName);
or with your actual code
var query = (from u in db.Users
join s in db.Songs
on u.UserId equals s.UserId
select new {
songId = s.SongId,
userId = s.UserId,
userName = u.Username
})//new anonymous object is possible in linq2entities
.ToList()
.Select(x => new HomeSongList(x.songId, x.userId, x.userName);
Related
I have the following many-to-many relation modelled
public class Profile
{
ICollection<Category> Categories { get; set;}
// One-To-Many
ICollection<Platform> Platforms { get; set; }
}
public class Category
{
ICollection<Profile> Profiles { get; set; }
}
public class ProfileCategory
{
public int ProfileId { get; set; }
public Profile Profile { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set;}
}
I'm using ASP.NET Core MVC and have a filter view model where a filter on some attributes on profile name and it works.
Trying to filter based on category proved much harder to implement (at least the solution isn't obvious to me :)
From the web the user can select zero, one or many categories to filter on so basically what is sent to my controller is a list of category ids.
IQueryable<Profile> query = _context.Profiles.Include(p => p.Categories).Include(p => p.Platforms);
if(string.IsNullOrEmpty(search.Name))
{
query = query.Where(p => p.Name.IndexOf(search.Name StringComparison.OrdinalIgnoreCase) > 0);
}
if(search.Categories?.Any() != null)
{
query = query.SelectMany(p => p.ProfileCategories)
.Join(search.Categories, pc => pc.CategoryId, cId => cId, (pc,_) => pc.Profile);
}
From this point the Profile object is different and other navigational properties such as Platforms is null hence breaking other parts.
How can I perform the join while retaining the original instance of Profile object. I first thought that they would be the same, but I was wrong.
Currently, EF Core JOINs are not perfect, and I recommend make two queries:
1) Select list of ProfileId (based on category list):
var profileIds = await _context.ProfileCategory
.Where(x => categoryIds.Contains(x.CategoryId)) // filtering goes here
.Select(x => x.ProfileId)
.Distinct()
.ToListAsync();
2) Select required data based on known IDs:
var result = await _context.Profiles
.Include(p => p.Categories).Include(p => p.Platforms)
.Where(x => profileIds.Contains(x.ProfileId))
.ToListAsync();
Yes, this is two queries instead one, but two simple queries, easily optimized using indexes.
I'm measuring database calls on a slow site using NHibernate profiler, and have noticed immediately that the following is causing a Select N+1 issue.
I've never used NHibernate so was hoping someone could help me out?
public virtual IQueryable<Employee> Employees()
{
return Session.Query<Employee>();
}
public IList<Employee> GetEmployeesByClientId(int clientId)
{
return Employees()
.Where(e => e.ClientId == clientId && e.Deleted == false)
.ToList();
}
At the point of calling ToList() a select statement is ran for every related record of EmployeeDetail, and I'm not sure why.
public virtual EmployeeDetail EmployeeDetail { get; set; }
You can use Fetch method to force a join sql statement to fill the property, for sample:
return Employees()
.Fetch(x => x.EmployeeDetail)
.Where(e => e.ClientId == clientId && e.Deleted == false)
.ToList();
I have a list which elements follows a sequence. I want use aggregate with OrderBy and ThenBy
List<string> list = new List<string> {"codCustomer", "customerName", "address1", "address2"};
list = list
.OrderBy(o => o.codCustomer)
.ThenBy(o => o.customerName)
.ThenBy(o => o.address1)
.ThenBy(o => o.addres2)
This list contain many elements and I want use aggregate with OrderBy and ThenBy, but I don't know how do it.
The matter is that this list is passed as a parameter and contains the fields to sort.
You should define a class called Customer with the properties Code, Name, Address1 and Address2 and create a list of customer objects, not a list of strings. Then you can order the list of customers the way you described.
class Customer
{
public string Code { get; set; }
public string Name { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
}
...
// at some point
List<Customer> customers = new List<Customer>();
customers.Add(new Customer()
{
Code = "cod1",
Name = "nam1",
Address1 = "ad1",
Address2 = "ad2"
};
// ... more customers
// you order customers the way you need
List<Customer> orderedCustomers = customers.OrderBy(p => p.Code).ThenBy(p => p.Name).ToList(); // you can continue ordering by all the properties if you want.
Here is a simplified example
public class Book
{
public string BookId
public string AuthorName
}
public class Author
{
public string Name
}
public class BookWithAuthor
{
public Book Book;
public Author Author;
}
For the sake of this example it is not possible to give Author an Id and use this as the foreign key.
List<Book> books = _unitOfWork.BookRepo.Get.ToList();
List<Author> authors= _unitOfWork.AuthorRepo.Get.ToList();
I am trying to write a LINQ join on Author.Name/Book.AuthorName fields and return a List of BookWithAuthor. All my attempts so far failed usually to do with "join parameters could not be determined..."
This should do it:
var results = _unitOfWork.BookRepo.Get
.Join(_unitOfWork.AuthorRepo.Get,
book => book.AuthorName,
author => author.Name,
(book, author) => new BookWithAuthor
{
Book = book,
Author = author
})
.ToList();
Use following
List<Book> books = _unitOfWork.BookRepo.Get.ToList();
List<Author> authors= _unitOfWork.AuthorRepo.Get.ToList();
List<BookWithAuthor> lst =
(from c in books
join d in authors on new {Aname = c.AuthorName } equals new {Aname = d.Name } into cd
from tcd in cd.DefaultIfEmpty()
select new BookWithAuthor
{
Book = c,
Author = tcd
}).ToList();
I have a table with about 3 items. I wish to populate the Dropdown List using that table. But i have a condition that i want to show only 2 items in the Dropdown List. How can it be done?
You could use a view model:
public class MyViewModel
{
public string SelectedItemId { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
and then take the first 2 items (obviously if your requirements is to take some other 2 items based on some condition you could chain with the .Where() extension method in order to filter first before calling .Take()):
public ActionResult Index()
{
var model = new MyViewModel();
model.Items = db.Items.Take(2).ToList().Select(x => new SelectListItem
{
Value = x.Id,
Text = x.SomeText
});
return View(model);
}
The view is standard stuff, simply call to the DropDownListFor helper:
#model MyViewModel
#Html.DropDownListFor(x => x.SelectedItemId, Model.Items)