Fluent NHibernate "Could not resolve property" - c#

I have read a lot of the questions about that same error but none since to match my exact problem. I'm trying to access the property of an object, itself part of a root object, using Fluent NHibernate. Some answers say I need to use projections, others that I need to use join, and I think it should work through lazy loading.
Here are my two classes along with the Fluent mappings:
Artist class
public class Artist
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Album> Albums { get; set; }
public virtual string MusicBrainzId { get; set; }
public virtual string TheAudioDbId { get; set; }
public Artist() { }
}
public class ArtistMap : ClassMap<Artist>
{
public ArtistMap()
{
LazyLoad();
Id(a => a.Id);
Map(a => a.Name).Index("Name");
HasMany(a => a.Albums)
.Cascade.All();
Map(a => a.MusicBrainzId);
Map(a => a.TheAudioDbId);
}
}
Album class
public class Album
{
public virtual int Id { get; set; }
public virtual Artist Artist { get; set; }
public virtual string Name { get; set; }
public virtual IList<Track> Tracks { get; set; }
public virtual DateTime ReleaseDate { get; set; }
public virtual string TheAudioDbId { get; set; }
public virtual string MusicBrainzId { get; set; }
public Album() { }
}
public class AlbumMap : ClassMap<Album>
{
public AlbumMap()
{
LazyLoad();
Id(a => a.Id);
References(a => a.Artist)
.Cascade.All();
Map(a => a.Name).Index("Name");
HasMany(a => a.Tracks)
.Cascade.All();
Map(a => a.ReleaseDate);
Map(a => a.TheAudioDbId);
Map(a => a.MusicBrainzId);
}
}
And the error happens when this code is interpreted:
var riAlbum = session.QueryOver<Album>()
.Where(x => x.Name == albumName && x.Artist.Name == artist)
.List().FirstOrDefault();
The error happens when Fluent NHibernate tries to resolve the x.Artist.Name value:
{"could not resolve property: Artist.Name of: Album"}
What would be the correct way of doing this?

You have to think of your QueryOver query as (nearly) directly translating into SQL. With this in mind, imagine this SQL query:
select
Album.*
from
Album
where
Album.Name = 'SomeAlbumName' and
Album.Artist.Name = 'SomeArtistName'
This won't work because you can't access a related table's properties like that in a SQL statement. You need to create a join from Album to Artist and then use a Where clause:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.List()
.FirstOrDefault();
Also, since you're using FirstOrDefault, you may want to consider moving that logic to the database end. Currently, you're pulling back every record matching your criteria and then taking the first one. You could use .Take to limit the query to 1 result:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.Take(1)
.SingleOrDefault<Album>();

Another explanation is that you are missing your mapping of this property or field in a NHibernateClassMapping definition. I came here about why I was getting this error based on the following scenario.
var query = scheduleRepository.CurrentSession().Query<Schedule>()
.Where(x => x.ScheduleInfo.StartDate.Date < dateOfRun.Date);
This was giving me a Could Not Resolve Property error for StartDate. This was a head scratcher, since I use this syntax all the time.
My mapping file was the following:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
}
}
which was missing the StartDate. Changed to:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
Map(x => x.StartDate);
}
}
Which resolved the error.

Related

Nested include Entity Framework Core

I'm using EF Core for my project. And I have a problem with nested query in EF Core.
I have 2 classes:
public class PermissionGroupDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<PermissionDefinitionEntity> PermissionDefinitions { get; set; }
}
public class PermissionDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public bool IsEnabled { get; set; }
public virtual string GroupName { get; set; }
public virtual PermissionGroupDefinitionEntity Group { get; set; }
public virtual ICollection<PermissionDefinitionEntity> Children { get; set; }
}
and this is the ApplicationDbContext:
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Group).WithMany(r => r.PermissionDefinitions).OnDelete(DeleteBehavior.Cascade);
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Parent).WithMany(r => r.Children).OnDelete(DeleteBehavior.Cascade);
I want query all PermissionGroupDefinitionEntity included PermissionDefinitionEntity and self referencing of PermissionDefinitionEntity.
Can I do that with EF Core?
You need to recursively load PermissionDefinitions that placed in the PermissionGroupDefinitionEntity.
First, you should load all PermissionGroupDefinitionEntities including its children using the following query :
var query = _dbContext.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(p => p.PermissionDefinitions )
.ThenInclude(p => p.Children)
.ToListAsync();
Since every PermissionGroupDefinitionEntity has a list of PermissionDefinition you need a nested loops like this code :
foreach (var PermissionGroupDefinitionEntity in PermissionGroupDefinitionEntities)
{
foreach (var PermissionDefinitions in PermissionDefinitions)
{
}
}
Then in the inner loop you should call your recursive function.
See following link (sample for get all children recursively in Entity Framework Core)
https://patrickdesjardins.com/blog/how-to-load-hierarchical-structure-with-recursive-with-entity-framework-5
This way has terrible performance and I don't recommend that.
In this case it's seems you must write a stored procedure in SQL for better performance.
You can use .ThenInclude(i => ...) like so
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Group)
.AsQueryable();
Edit:
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Children)
.AsQueryable();

Entity Framework Include Nest collection and nested properties [duplicate]

This question already has an answer here:
EFCore Linq ThenInclude Two Foreign Keys To Same Table
(1 answer)
Closed 5 years ago.
I have 4 classes
public class Customer
{
public string CustomerName { get; set; }
public ICollection<Order> Orders{ get; set; }
}
public class Order
{
public string OrderNumber { get; set; }
public ICollection<OrderLine> OrderLines { get; set; }
public OrderType Type { get; set; }
}
public class OrderLine
{
public string StockItem { get; set; }
}
public class OrderType
{
public string Type { get; set; }
}
Using entity frameworks i want to pull out all the information starting at Customer level. I can get most information out but i am stuck getting OrderType to show against the order.
This is what i have so far.
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.ThenInclude(l => l.OrderLines)
I've tried added select after the o.Orders but that doesn't seem to work.
You can do something like this:
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.Include(o => o.Orders.Select(l => l.OrderLines))
.Include(o => o.Orders.Select(t => t.Type))
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.ThenInclude(l => l.OrderLines)
.Include(o => o.Orders).ThenInclude(t => t.Type)
Adding another Include after the OrderLines and then selecting the type seemed to work.

Query over many-to-many entity with nhibernate

I kinda need a brainstorm here..
Here's my scenario:
public class UserSystem
{
public virtual User User { get; set; }
public virtual System System { get; set; }
}
public class User
{
public User() { }
public virtual int UsrId { get; set; }
}
public class System
{
public System() { }
public virtual decimal SistId { get; set; }
public virtual IList<Perf> SystPerf { get; set; }
}
public class Perf
{
public Perf() { }
public virtual int PerfId { get; set; }
public virtual System System { get; set; }
public virtual string Perf_Adm_Portal { get; set; }
}
I need to get all the users that has Perf_Adm_Portal == "S". I know its kinda simple but I am doing something wrongly...
I tryed this:
var list = session.Query<UserSystem>()
.Fetch(x => x.User)
.Fetch(x => x.System)
.ThenFetch(x => x.SystPerf)
.Where(x => x.System.SistId == someId)
//.Where(x => x.Sistema.SystPerf.Where(x => x.Perf_Adm_Portal == "S"))
.ToList<USerSystem>();
that commented line its just what I want but it doesn't work... doesn't even compile. So this query returns me all users (including those who has the flag Perf_Adm_Portal != "S") and then I just treat them in memory... but its taking so long to execute this query and I know that there is a better solution... can you guys help me? Regards
**Edit
Nevermind, guys... I just realize that I have a third table (UserPerf).
And each perf has its own System.
So I retrieve all admins of a system like this:
var list = session.Query<UserPerf>()
.Where(up => up.Perf.Perf_Adm_Portal.ToLower().Equals("yes"))
.Where(up => up.Perf.System.SistId == sistId)
.Select(up => up.User)
.ToList<User>();
Sorry for the trouble...
#Radim Köhler, thanks for your time! But I think what I was trying to do it's impossible without that third table below.
Regards,
I would say, that this kind of query should work for you:
var inner = session.Query<UserSystem>()
.Where(e => e.System.SystPerf.Any(p => p.Perf_Adm_Portal == "S"))
.Select(e => e.User.UserId);
var query = session.Query<User>()
.Where(i => inner.Contains(u.userId));
var list = query
// .Skip(y).Take(x) // paging
.ToList();

Postrgresql One-To-Many with MVC3 and jTable

I'm a newbie in MVC apps, and I've encountered a very specific problem. Thing is, I have 2 classes: "Paciente" and "Analises".
public class Paciente
{
public virtual Guid PacienteID { get; set; }
public virtual string Nome { get; set; }
public virtual string Sexo { get; set; }
public virtual DateTime DataDeNasc { get; set; }
public virtual int IdadeDiag { get; set; }
public virtual IList<Analises> analises { get; set; }
}
public class Analises
{
public virtual Guid AnaliseID { get; set; }
// some analysiss values
public virtual decimal afp { get; set; }
public virtual decimal hemoglobina { get; set; }
public virtual DateTime DataDaAnalise { get; set; }
public virtual Paciente pac { get; set; }
}
So, a Patient has many Analysis, and each Analysis has one Patient (on-to-many).
I have mapped this with NHIbernate and FluentNHibernate:
public PacienteMap()
{
Table("pacientes");
Id(x => x.PacienteID).GeneratedBy.Guid();
Map(x => x.Nome);
Map(x => x.Sexo);
Map(x => x.DataDeNasc).CustomType("Date");;
Map(x => x.IdadeDiag);
HasMany(m => m.analises).Not.KeyNullable().Fetch.Join().KeyColumn("pacienteid");
}
public AnalisesMap()
{
Table("analises");
Id(x => x.AnaliseID).GeneratedBy.Guid();
Map(x => x.afp);
Map(x => x.hemoglobina);
Map(x => x.DataDaAnalise).CustomType("Date");
References(x => x.pac).ForeignKey("pacienteid").Not.Nullable();
}
My problem is that I'm using jTable to show this. I want to see a list of Patients (and it works), and then, a list of analysis for each patient (doesn't work!)
My Controller goes like this:
[HttpPost]
public JsonResult AnalisesList(Guid pacienteId)
{
try
{
var list_analises = AnalisesRepository.GetPacienteAnalises(pacienteId);
var all_analises = Mapper.Map<IEnumerable<Analises>, IEnumerable<AnalisesView>>(list_analises);
List<AnalisesView> analises = all_analises.ToList();
return Json(new { Result = "OK", Records = analises });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
I am also using automapper to go from the Object Views from Database Objects.
So, rigth now, my analysis list does not show! I don't know why. The GetPacienteAnalises is like:
public IList<T> GetPacienteAnalises (Guid pacienteId)
{
using (ISession session = SessionManager.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
return session.CreateCriteria(typeof(T)).Add(Restrictions.Eq("pacienteid", pacienteId)).List<T>();
}
}
}
So, I think everything is Okay... But I keep receiving a "could not resolve property: pacienteid of: Infraestrutura.Models.Analises".
Your problem is in this line:
return session.CreateCriteria(typeof(T))
.Add(Restrictions.Eq("pacienteid", pacienteId))
.List<T>();
I assume that T in this case is Analises. Analises does not have a property named pacienteid. Instead, it has a property named pac. Replace "pacienteid" with "pac.PacienteID". The fields that you use when creating the criteria should be the property names, not the column names.
return session.CreateCriteria(typeof(T))
.Add(Restrictions.Eq("pac.PacienteID", pacienteId))
.List<T>();
If you were filtering by some column other than Paciente's primary key, Nome for example, then a join would be necessary. In that case, you would do something like:
session.CreateCriteria(typeof(T))
.CreateAlias("pac", "p") // CreateAlias performs an inner join
.Add(Expression.Eq("p.Nome", name))
.List<T>()
But no join is needed to get to pac.PacienteID.

HasMany Mapping but getting one element or get some of

public class Category
{
public virtual int Id { set; get; }
public virtual string Name { set; get; }
public virtual int CategoryOrder { set; get; }
public virtual IEnumerable<News> LatestNews { set; get; }
}
public sealed class CategoryMap :ClassMap<Category>
{
public CategoryMap()
{
LazyLoad();
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.CategoryOrder);
HasMany(x => x.LatestNews);
}
}
IRepository<Category> newsRepo = new NHibernateRepository<Category>();
using(var session = newsRepo.GetSessionFactory().OpenSession())
using(var transaction = session.BeginTransaction())
{
var result = session.Query<Category>().OrderBy(x => x.CategoryOrder);
transaction.Commit();
}
I have this category class Which I want to display a (only one) News per category. Is this correct mapping? or should i change it to Map
When i run this, it gets all the news per category. But i want the latest news per category (only one). I can get the latest news by querying News.DateUpdated.
How should i change the query to get one news per category?
or how do I get some of the News? ie: limit the number of news I can query?
I think you can have your cake and eat it too. You have a list of News items, so just leave that. But add in another property that gets just the News item you want.
public class Category
{
public virtual int Id { set; get; }
public virtual string Name { set; get; }
public virtual int CategoryOrder { set; get; }
public virtual IEnumerable<News> AllNews { set; get; }
public virtual News LatestNews
{
get
{
// probably needs some work
return this.AllNews.OrderByDescending(n => n.SomeDateField).Take(1);
}
}
}
public sealed class CategoryMap :ClassMap<Category>
{
public CategoryMap()
{
LazyLoad();
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.CategoryOrder);
HasMany(x => x.AllNews);
}
}
you can use lazy mode extra to achieve this,
public News GetLatestNews(Category cat)
{
var session = SessionFactory.CurrentSession;
var news = session.CreateFilter(cat.LatestNews , "order by categoryorder").SetFirstResult((page - 1) * pageSize).SetFirstResult(0).SetMaxResults(1).List().FirstOrDefault();
return news;
}
however if you try to access the LatestNews collection directly, this won't be of any use as it will load all the news objects associated with Category from db which would lower the performance.

Categories

Resources