I have a list of Cutdetails. I am trying to write a function using LINQ that will return the count of bars in the list where the CODE , BRAND, CODE and LENGTH all match. I want to be able to specify all these parameters and return a number for the number of matches.
I have tried using foreach statements which is fine but i'm sure there is an neater and smarter way to do it using LINQ. Any suggestions?
List<Bar> bars = new List<Bar>();
public class Bar
{
public string Brand { set; get; }
public string System { set; get; }
public string Code { set; get; }
public string Length { set; get; }
}
Thanks in advance!
Will
You can filter using the match and then do a count.
var occurences = bars.Where(x => x.Brand == "Brand" && x.Code == "code").Count();
My case: I have a bunch of variables. These variables are shared between all Environments. In each Environment these variables have several values, each value connected to a "EnvironmentVariableSet". The amount of EnvironmentVariableSets vary from Environment to Environment. The number of variables are fixed.
For a given Environment I want to display, in a Grid, all the variables, and for each EnvironmentVariableSet, the value of each variable. Something like this:
Column 1 : The variables
Column 2 : The values of the variable in set1
...
Column N : The values of the variable in setN
The goal is that for each variable, the user can edit the value of that variable in a given EnvironmentVariableSet for a given Environment.
My Models:
public class VariableValues
{
public int VariableID { get; set; }
public string VariableName { get; set; }
public List<EnvironementVariableSetValue> environmentSetValues { get; set; }
}
public class EnvironementVariableSetValue
{
public String EnvironmentVariableValue { get; set; }
public int VariableID { get; set; }
}
My Grid
#(Html.Telerik().Grid(variableValues)
.Name("EnvironmentVariables")
//blabla
.Columns(columns =>
{
columns.Bound(v => v.VariableName).Width(50).ReadOnly();
//I NEED A WAY TO BIND EACH COLUMN TO THE UNDEFINED NUMBER ENVIRONMENTVARIABLESETS WITH VALUES HERE! SOMETHING LIKE THIS
//columns.Bound(v => v.ValueForSetN).Whitd(50);
})
//blabla
)
Maybe Im just stupid, but i cant find a good solution for this.
Thanks
Given the following code:
class Book {
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Review> Reviews { get; set; }
}
class Review {
public int ID { get; set; }
public int? Overall { get; set; }
public string Comment { get; set; }
}
And assuming I have a list of every book in the database:
var books; // List<Book>
I would like to show the user the books ordered by the average review. Ideally, the list of books would have the highest average review at the top and sort descending. Given I have the overall rating in Review as a null-able integer, some books will not have reviews, let alone an overall score. I'm having difficulty sorting this data, I've tried seemingly every combination of:
books.OrderByDescending(b => b.Reviews.Average(a => a.Overall));
Is it possible to sort my list without creating a second list to get something like:
Book A (4.5/5)
Book B (4.3/5)
Book C (3.9/5)
Book D (3.5/5)
Book E (-/5)
Book F (-/5)
Thanks in advance.
You should be able to use this:
books.OrderByDescending(b =>
(b.Reviews == null) ? 0:b.Reviews.Average(r => r.Overall??0));
Thank you TheCodeKing for point me in the right direction. Here is what fixed the problem:
books.OrderByDescending(b => b.Reviews.Count() == 0 ? 0 : b.Reviews.Average(r => r.Overall ?? 0));
I am using ASP.NET MVC 3 with Entity Framework 4 using POCOs and want to query a set and select some properties to put into my viewModel. I will sketch a simplified version of my situation:
Situation:
I have an entity BananaTree containing a collection of Banana
public class Banana
{
public int Id { get; set; }
public int Size { get; set; }
public TimeSpan Age { get; set }
public string Description { get; set; }
}
public class BananaTree
{
public int Id { get; set; }
public ICollection<Banana> Bananas { get; set; }
}
I also have a view model BananaListItemViewModel used in the view showing a list of bananas for a certain banana tree. This view is managed by the BananaTreeController
public class BananaListItemViewModel
{
public int Id { get; set; }
public TimeSpan Age { get; set }
}
I have a Details action on the controller like so:
public ActionResult Details(int bananaTreeId)
{
var viewModel = from bananaTree in bananaTreeRepository.BananaTrees
where bananaTree.Id == bananaTreeId
from banana in bananaTree.Bananas
select new BananaListItemViewModel
{
Id = banana.Id,
Age = banana.Age
};
return View(viewModel);
}
What I want to change
This works fine and now I only select the items from the database that I need for my view model. However, I want to take out some more logic from my controller and am trying to do this as much as possible.
I would like to have a function in my repository like so:
IQueryable<Banana> GetBananas(int bananaTreeId)
{
return (from bananaTree in BananaTrees
where bananaTree.Id == bananaTreeId
select bananaTree.Bananas).Single().AsQueryable();
}
and use it like so:
public ActionResult Details(int bananaTreeId)
{
var viewModel = from banana in bananaTreeRepository.GetBananas(bananaTreeId)
select new BananaListItemViewModel
{
Id = banana.Id,
Age = banana.Age
};
return View(viewModel);
}
Question
My question is, in this case, will the two queries be combined and go to the database in one go like in my first example or will this first get all the bananas from the tree completely out of the database and perform the second query on that list? I would prefer the first case. If not, could I rewrite the GetBananas query to get that behaviour (for example like the query below)?
IQueryable<Banana> GetBananas(int bananaTreeId)
{
return from bananaTree in BananaTrees
where bananaTree.Id == bananaTreeId
from banana in bananaTree.Bananas
select banana;
}
Thanks very much in advance.
In your specific case, it will be only one query, if the call to Single() doesn't lead to the query to be executed. Unfortunately, I couldn't find any info on whether it does or does not. The call to AsQueryable does not trigger the execution as long, as the Bananas property really is an IQueryable.
According to http://msdn.microsoft.com/en-us/library/bb156472.aspx, the call to Single doesn't execute your query.
Conclusion:
You code should result in only one query.
In general:
You can pass an IQueryable from one method to another without it being implicitly executed.
The following code will result in only one SQL statement executed at the end, when the call to ToList happens:
IQueryable<Banana> GetBananasByWeight(int weight)
{
return from banana in Bananas where banana.Weight = weight;
}
IQueryable<Banana> FilterByQuality(IQueryable<Banana> bananaQuery, int quality)
{
return bananaQuery.Where(b => b.Quality == quality);
}
public List<Banana> GetBananas(int weight, int quality)
{
var query = GetBananasByWeight(weight);
var filteredBananas = FilterByQuality(query, quality);
return filteredBananas.ToList();
}
The basic problem...
I have a method which executes the following code:
IList<Gig> gigs = GetGigs().WithArtist(artistId).ToList();
The GetGigs() method gets Gigs from my database via LinqToSql...
So, when GetGigs().WithArtist(artistId).ToList() is executed I get the following exception:
Member access 'ListenTo.Shared.DO.Artist Artist' of 'ListenTo.Shared.DO.Act' not legal on type 'System.Collections.Generic.List`1[ListenTo.Shared.DO.Act]
Note that the extension function "WithArtist" looks like this:
public static IQueryable<Gig> WithArtist(this IQueryable<Gig> qry, Guid artistId)
{
return from gig in qry
where gig.Acts.Any(act => (null != act.Artist) && (act.Artist.ID == artistId))
orderby gig.StartDate
select gig;
}
If I replace the GetGigs() method with a method that constructs a collection of gigs in code (rather than from the DB via LinqToSQL) I do NOT get the exception.
So I'm fairly sure the problem is with my LinqToSQl code rather than the object structure.
However, I have NO IDEA why the LinqToSQl version isnt working, so I've included all the associated code below. Any help would be VERY gratefully receivced!!
The LinqToSQL code....
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
return from g in DBContext.Gigs
let acts = GetActs(g.ID)
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs()
{
return from a in DBContext.Acts
join artist in DBContext.Artists on a.ArtistID equals artist.ID into art
from artist in art.DefaultIfEmpty()
select new ListenTo.Shared.DO.Act
{
ID = a.ID,
Name = a.Name,
Artist = artist == null ? null : new Shared.DO.Artist
{
ID = artist.ID,
Name = artist.Name
},
GigId = a.GigID
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs(Guid gigId)
{
return GetActs().WithGigID(gigId);
}
I have included the code for the Act, Artist and Gig objects below:
public class Gig : BaseDO
{
#region Accessors
public Venue Venue
{
get;
set;
}
public System.Nullable<DateTime> EndDate
{
get;
set;
}
public DateTime StartDate
{
get;
set;
}
public string Name
{
get;
set;
}
public string Description
{
get;
set;
}
public string TicketPrice
{
get;
set;
}
/// <summary>
/// The Act object does not exist outside the context of the Gig, therefore,
/// the full act object is loaded here.
/// </summary>
public IList<Act> Acts
{
get;
set;
}
#endregion
}
public class Act : BaseDO
{
public Guid GigId { get; set; }
public string Name { get; set; }
public Artist Artist { get; set; }
}
public class Artist : BaseDO
{
public string Name { get; set; }
public string Profile { get; set; }
public DateTime Formed { get; set; }
public Style Style { get; set; }
public Town Town { get; set; }
public string OfficalWebsiteURL { get; set; }
public string ProfileAddress { get; set; }
public string Email { get; set; }
public ImageMetaData ProfileImage { get; set; }
}
public class BaseDO: IDO
{
#region Properties
private Guid _id;
#endregion
#region IDO Members
public Guid ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
}
}
I think the problem is the 'let' statement in GetGigs. Using 'let' means that you define a part of the final query separately from the main set to fetch. the problem is that 'let', if it's not a scalar, results in a nested query. Nested queries are not really Linq to sql's strongest point as they're executed deferred as well. In your query, you place the results of the nested query into the projection of the main set to return which is then further appended with linq operators.
When THAT happens, the nested query is buried deeper into the query which will be executed, and this leads to a situation where the nested query isn't in the outer projection of the query to execute and thus has to be merged into the SQL query ran onto the DB. This is not doable, as it's a nested query in a projection nested inside the main sql query and SQL doesn't have a concept like 'nested query in a projection', as you can't fetch a set of elements inside a projection in SQL, only scalars.
I had the same issue and what seemed to do the trick for me was separating out an inline static method call that returned IQueryable<> so that I stored this deferred query into a variable and referenced that.
I think this is a bug in Linq to SQL but at least there is a reasonable workaround. I haven't tested this out yet but my assumption is that this problem may arise only when referencing static methods of a different class within a query expression regardless of whether the return type of that function is IQueryable<>. So maybe it's the class that holds the method that is at the root of the problem. Like I said, I haven't been able to confirm this but it may be worth investigating.
UPDATE: Just in case the solution isn't clear I wanted to point it out in context of the example from the original post.
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
var acts = GetActs(g.ID); // Don't worry this call is deferred
return from g in DBContext.Gigs
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
Note that while this should correct the issue at hand there also seems to be another issue in that the deferred acts query is being accessed in each element of the projection which I would guess would cause separate queries to be issued to the database per row in the outer projection.
I don't see anything in your classes to indicate how LINQ to SQL is meant to work out which column is which, etc.
Were you expecting the WithArtist method to be executed in .NET, or converted into SQL? If you expect it to be converted into SQL, you'll need to decorate your Gig class with appropriate LINQ to SQL attributes (or configure your data context some other way). If you want it to be executed in code, just change the first parameter type from IQueryable<Gig> to IEnumerable<Gig>.
I found out that an issue like this (which I also had recently) can be resolved, if you convert the IQueryable (or Table) variable Gigs into a list like so
return from g in DBContext.Gigs.ToList()
...
If that still doesn't work, do the same for all the IQueryables. The reason behind seems to me that some queries are too complex to be translated into SQL. But if you "materialize" it into a list, you can do every kind of query.
Be careful, you should add "filters" (where conditions) early because too much memory consumption can become a problem.