I am trying to get a group by followed by an order by count to work but I keep getting a 'Antlr.Runtime.NoViableAltException' being thrown.
Here is the simplest error case I can create.
var results = ArticleStatsRepository.GetAll().GroupBy(x => x.Article.ArticleId)
.OrderBy(x => x.Count());
ArticleStatsRepository.GetAll() returns an IQueryable of ArticleStats.
public class ArticleStats
{
public virtual int ArticleStatsId { get; set; }
public virtual Article Article { get; set; }
public virtual User Viewer { get; set; }
public virtual ArticleStatTypeEN ArticleStatType { get; set; }
public virtual DateTime DateTime { get; set; }
}
Ultimately I would like the following query to execute.
return ArticleStatsRepository.GetAll()
.Where(x => x.DateTime > DateTime.Now.Add(-timeSpan))
.Where(x => x.ArticleStatType == ArticleStatTypeEN.View)
.GroupBy(x => x.Article.ArticleId)
.Select(x => new { ArticleId = x.Key, Count = x.Count() })
.OrderByDescending(x => x.Count)
.Join(ArticleRepository.GetAll(), artStats => artStats.ArticleId, articles => articles.ArticleId, (artStats, articles) => new MostPopularArticleResult { ArticleId = artStats.ArticleId, ArticleTitle = articles.Content.Title, Count = artStats.Count });
I am using Fluent NHibernate 1.2.0.712 which references NHibernate: 3.1.0.4000.
Any help would be greatly appreciated!
Regards
Steve
Update: This is how I got round the issue. Not perfect as I didn't want to start using HQL with its QueryOver and would of liked to stick to IQueryable throughout.
public virtual IQueryable<MostPopularArticleResult> GetMostPopularArticleResults(TimeSpan timeSpan, IQueryable<Article> filteredArticles, List<ArticleStatTypeEN> types, int take)
{
var results = ArticleStatsRepository.GetAllQueryOver().Where(x => x.DateTime > DateTime.Now.Add(-timeSpan));
results = results.Where(x => x.ArticleStatType.IsIn(types));
var articleIdsWithCounts = results.Select(
Projections.Group<ArticleStats>(x => x.Article.ArticleId),
Projections.Count<ArticleStats>(x => x.Article.ArticleId))
.OrderBy(Projections.Count<ArticleStats>(x => x.Article.ArticleId))
.Desc
.Take(take)
.List<object[]>()
.Select(x => new { ArticleId = (int)x[0], Count = (int)x[1] });
return articleIdsWithCounts.Join(filteredArticles, artStats => artStats.ArticleId, articles => articles.ArticleId, (artStats, articles) => new MostPopularArticleResult { ArticleId = artStats.ArticleId, ArticleTitle = articles.Content.Title, Count = artStats.Count })
.AsQueryable();
}
As #mynkow and #Chris S said that was a NH 3.1 issue. You could update library version or look at thoose question about simila problem:
http://sourceforge.net/p/nhibernate/mailman/nhibernate-issues/
Nhibernate 3 Linq throws Antlr.Runtime.NoViableAltException
NHibernate Query, using OfType and Subqueries
NHibernate 3.1 migration problem with Linq
Related
When trying to keep the Cyrillic alphabet in the database, only inverted question marks are saved in the database.
The model looks like this
public class Roles {
public string Name { get; set; }
public string Appname { get; set; }
public string Description { get; set; }
}
Creating a context like this
modelBuilder.Entity<Roles>(entity =>
{
entity.HasKey(e => new { e.Name, e.Appname });
entity.ToTable("ROLES");
entity.HasIndex(e => new { e.Name, e.Appname })
.HasName("PK_ROLES")
.IsUnique();
entity.Property(e => e.Name)
.HasColumnName("NAME")
.HasMaxLength(100)
.IsUnicode(false);
entity.Property(e => e.Appname)
.HasColumnName("APPNAME")
.HasMaxLength(20)
.IsUnicode(false);
entity.Property(e => e.Description)
.HasColumnName("DESCRIPTION")
//.HasMaxLength(256)
//.IsUnicode(true);
.HasColumnType("NVARCHAR2(256)");
});
Tried and what is commented out and the next line
The table looks created correctly
And the execution of this code
using (var context = new FrontContext())
{
var roles = context.Roles.ToList();
var role = new Roles{
Name = $"ADMINS{DateTime.Now.ToString("HHmmss")}",
Appname = "TESTAPP",
Description = "тестовая роль"};
context.Add(role);
context.SaveChanges();
}
...gives such a picture
Check thread:4291600#Oracle, they fixed it.
Just update Oracle.EntityFrameworkCore to 2.19.80 then you are good to go.
Remove .IsUnicode(false);
Ref:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.builders.propertybuilder-1.isunicode?view=efcore-5.0
I want to extend this query..
var itinerary = context.Itineraries
.Include(i => i.ItineraryDays)
.Include(i => i.ItineraryStartDates)
.Where(i => i.Id == Id).FirstOrDefault();
.. to include 3 tables which are accessible through ItineraryDays using the following navigation properties to traverse.
ItineraryDay>> public virtual Listing Listing { get; set;} (ItineraryDay.ListingId === Listing.Id)
Listing>> public virtual Port Port { get; set; } (Listing.PortId === Port.Id)
Port>> public virtual Country Country { get; set; } (Port.CountryId = Country.Id)
regards, Guy
in that case, you have to replace
.Include(i => i.ItineraryDays)
by
.Include(i => i.ItineraryDays.Listing.Port.Country)
And you will have the 4.
As they are "single" properties, that will do the trick.
EDIT
oh, as ItineraryDays in Itinerary (could have guess, by the way) seems to be a collection , think you have to do
.Include(i => i.ItineraryDays.Select(id => id.Listing.Port.Country))
A Theft has an action property
This is the query i'm trying to get NHibernate.Linq to produce:
SELECT * FROM `thefts`
LEFT JOIN memberThefts
ON thefts.id = memberThefts.theftId AND memberThefts.memberId = 1
I want to get a list of all the thefts where action.memberId == some number or just null if it doesn't find a row, simple as a query yet it's been giving me a nightmare all day!
thefts = session.Query<Theft>()
.Fetch(x => x.action)
.Where(x => x.action.memberId == member.id)
.ToList();
This executes the following SQL:
select theft0_.id as id9_0_,
memberthef1_.memberId as memberId7_1_,
theft0_.name as name9_0_,
theft0_.chance as chance9_0_,
memberthef1_.theftId as theftId7_1_,
memberthef1_.availableTime as availabl3_7_1_
from thefts theft0_
left outer join memberThefts memberthef1_
on theft0_.id = memberthef1_.theftId,
memberThefts memberthef2_
where theft0_.id = memberthef2_.theftId
and memberthef2_.memberId =1 /* ?p0 */
The theft class:
public class Theft
{
public virtual byte id { get; set; }
public virtual string name { get; set; }
public virtual byte rank { get; set; }
public virtual byte chance { get; set; }
public virtual MemberTheft action { get; set; }
...
And it's mapping:
public TheftMap()
{
Table("thefts");
Id(x => x.id);
Map(x => x.name);
Map(x => x.id);
Map(x => x.chance);
References(x => x.action)
.Nullable()
.PropertyRef(x => x.theftId)
.Column("id");
}
Any solution will do HQL, QueryOver etc
It can't be done using the LINQ provider, but you can do it with QueryOver. Something along the lines of:
MemberTheft memberAlias = null;
var result = Session.QueryOver<Theft>()
.Left.JoinQueryOver(x => x.action, () => memberAlias)
.Where(() => memberAlias.memberId == member.id);
Edit: Updated Query.
I have a class structure like below.
ObservableCollection<Group> deviceCollection = new ObservableCollection<Group>();
public class Group
{
public string Name { get; set; }
public List<TargetSelectionStructure> TargetCollection { get; set; }
}
public class TargetSelectionStructure
{
public string ItemId { get; set; }
public string Name { get; set; }
public bool IsGroup { get; set; }
}
From the observable collection object deviceCollection. I need to get the collection which matches with IsGroup property as false. So I have written like
var currentStruct = deviceCollection.Where(d => d.TargetCollection.Any(t => t.IsGroup == false));
Now the currentStruct should contain the collection basically List<TargetSelectionStructure>. I am unable to cast the currentStruct to the type of List<TargetSelectionStructure>.
How can I solve this?
You can't cast it, because currentStruct is an IEnumerable<Group>.
I think you query needs to look like this:
var currentStruct = deviceCollection.SelectMany(x => x.TargetCollection)
.Where(x => !x.IsGroup).ToList();
This returns all TargetSelectionStructure instances from all Groups that have IsGroup == false.
Your question is not entirely clear. It is possible to interpret your question in a second way: You want to have all TargetSelectionStructure instances from a Group if at least one of them has IsGroup == false.
To achieve this, you would use this query:
var currentStruct = deviceCollection.Where(x => x.TargetCollection
.Any(y => !y.IsGroup))
.SelectMany(x => x.TargetCollection)
.ToList();
My object hierarchy is like this:
class Detail
{
public List<Row> Rows { get; set; }
}
class Row
{
public List<Column> Columns { get; set; }
}
class Column
{
public string Name { get; set; }
public bool IsUpdated { get; set; }
}
I want to set column.IsUpdated = true where column.Name = "id".
I am trying this, it doesn't work.
detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Select(c => c.IsUpdated = true));
The philosophy of LINQ is to not have side effects. That's why on purpose it does not make it easy for you to do this. You could do it either with a classic
var cols = details.Rows.SelectMany(r => r.Columns)
.Where(c => c.Name.ToLower().Equals("id"));
foreach(var col in cols) {
col.IsUpdated = true;
}
or by using List.ForEach, but in a different manner:
details.Rows.SelectMany(r => r.Columns)
.Where(c => c.Name.ToLower().Equals("id")).ToList()
.ForEach(c => { c.IsUpdated = true });
LINQ is really intended for querying data, not changing values within the data. If you want to make an entire new detail item, you could do that:
var newDetail = new Detail
{
Rows = detail.Rows.Select(r => new Row
{
Columns = r.Columns.Select(c => new Column { Name = c.Name, IsUpdated = c.Name.ToLower() == "id" ? true : c.IsUpdated }).ToList()
})
.ToList()
};
Note that the above would be cleaner, most likely, if you added constructors for your types, as well.
That being said, if you want to update it in place, like you were showing, I would just use loops:
foreach(var row in detail.Rows)
foreach(var col in row.Columns)
if (col.Name.ToLower() == "id")
col.IsUpdated = true;
I find that far easier to follow, especially in this case.
You shouldn't mutate all elements of a collection with LINQ (although it can be done, see this question). It's simpler and more readable to just use a vanilla foreach.
foreach (var row in detail.Rows)
foreach (var col in row.Columns)
if (c.Name.ToLower().Equals("id"))
c.IsUpdated = true;
I just got it to work like this. Is it inefficient? Instead of .Select I put .Any. It works, but I'm not sure if it's inefficient on a large data. If yes, I can go with one of the answers.
detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Any(c => c.IsUpdated = true));
Give this a try. This should update "IsUpdated" now with the same concept you were already trying to use.
detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Select(c => {c.IsUpdated = true; return c;}));