Query over many-to-many entity with nhibernate - c#

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();

Related

How to get last Id that contain HardwarId

I got an issue to reclaim Hardware that has exact ID (e.g. ID=5). There is my code:
class HardwareTransfer{
public int Id { set; get; }
public ICollection<Hardware> Hardwares { get; set; }
}
class Hardware{
public int Id { set; get; }
public string Title { set; get; }
}
How to get last HardwareTransfer.Id of HardwareTransfer, that contains Hardwares.Id = 5?
you can use this code
//_listHardwareTransfer is a List Of HardwareTransfer
var maxId=_listHardwareTransfer.Where(x => x.Hardwars.Contains(5)).Max(x => x.Id);
There are several ways how you can obtain this. By using LINQ (preferred way):
myHardwareTransfer.Hardwares.Last(a => a.Id == 5);
In C#:
Hardware lastFound;
foreach(var nHardware in myHardwareTransfer.Hardwares)
if(nHardware.Id == 5)
lastFound = nHardware;

Get columns from Tables (Using Inner Join) with DetachedCriteria (prefereable) or QueryOver

I'm having a problem with my query in NHibernate With DetachedCriteria (or using Session QueryOver). I need to get only a few columns from each table, like the classes as follow:
public class PanelaCorrida : BaseEntity
{
public virtual Int64? CodCorrida { get; set; }
public virtual Int64 NumLocal { get; set; }
public virtual Boolean IdcInspecionada { get; set; }
public virtual String Sigla { get; set; }
public virtual String Rota { get; set; }
public virtual Local Local { get; set; }
public virtual Panela Panela { get; set; }
}
public class Panela : BaseEntity
{
public Panela()
{
this.Local = new Local();
this.PanelaCorridas = new List<PanelaCorrida>();
}
public virtual Int32 Numero { get; set; }
public virtual Boolean IdcAtiva { get; set; }
public virtual Int16 Status { get; set; }
public virtual ICollection<PanelaCorrida> PanelaCorridas { get; set; }
public virtual Local Local { get; set; }
#endregion
}
public class Local : BaseEntity
{
public Local()
{
this.PanelaCorridas = new List<PanelaCorrida>();
this.Panelas = new List<Panela>();
}
#region Property
public virtual Int32 IdLocal { get; set; }
public virtual Int32 AreaLocal { get; set; }
public virtual String Descricao { get; set; }
public virtual String Codigo { get; set; }
#endregion
}
Basically, the Entity 'Panela' is the Mother of 'PanelaCorrida' (Each Panela can have multiple PanelaCorrida's) but a 'PanelaCorrida' can be in one Local. But one local can have multiple PanelaCorrida's as well. It's basically, this relationship:
Panela 1 - N PanelaCorrida
Local 1 - N Panela
Local 1 - N PanelaCorrida
For this query, i need to get the Last PanelaCorrida of the db, but i need the info of Panela and Local as well.
So far, i can get all data using this NHibernate query:
To get all id's of 'panela' which is active:
var panelaIdList = Session.QueryOver<Panela>()
.Select(c => c.Id)
.Where(c => c.IdcAtiva == true)
.List<Int64>();
To get all last id's of 'PanelaCorrida' which is active (and the last PanelaCorrida generated):
var corridaPanelaIdList = Session.QueryOver<PanelaCorrida>()
.Select(
Projections.Max<PanelaCorrida>(x => x.Id),
Projections.Group<PanelaCorrida>(x => x.Panela)
)
.Where(p => p.Panela.IsIn(panelaIdList.ToArray()))
.List<Object[]>();
Now, to get the result with all info of all those tables:
With DetachedCriteria:
criteria = DetachedCriteria.For<PanelaCorrida>()
.CreateAlias("Local", "L")
.CreateAlias("Panela", "P")
.Add(Restrictions.In("Id", corridaPanelaIdList.Select(x => x[0]).ToArray()));
With Session QueryOver:
var corridas = Session.QueryOver<PanelaCorrida>()
.Where(p => p.Id.IsIn(corridaPanelaIdList.Select(x => x[0]).ToArray()))
.List<PanelaCorrida>();
But the problem is, i need only a few columns of each Table. With NHibernate i've tried with Projections, and with QueryOver, i've tried with SelectList, but each time they generate a error (could not found the property of ...) or they do not populate the entities in result.
How i could achieve this?
Note: This is my query in first place (in SQL):
select cd.num_panela_corrida, cd.num_panela, p.numero, l.num_local from scp_panela_corrida cd
inner join scp_panela p on p.num_panela = cd.num_panela
inner join scp_local l on l.num_local = cd.num_local and cd.num_panela_corrida
in (
select
max( c.num_panela_corrida) as num_panela_corrida
from
scp_panela_corrida c
inner join
scp_panela p on p.num_panela = c.num_panela
and p.num_panela in (
select
num_panela
from
scp_panela
where
idc_ativa = 1
) group by c.num_panela ) order by cd.num_panela_corrida desc
But the client don't want to use a Stored Procedure or HQL.
Any help is welcome.
Resolved with the following code (provided with the link of Radim Kohler).
Radim, if you want, please answer this question with the provided link and i'll accept it as the answer. Thanks for your help.
Panela panela = null;
Local local = null;
var query = session.QueryOver<PanelaCorrida>()
.JoinAlias(c => c.Panela, () => panela)
.Where (c => c.Id.IsIn(corridaPanelaIdList.ToArray()))
.SelectList(list => list
.Select(c => c.Id))
.Select(c => c.CodCorrida)
.Select(Projections.Property(() => panela.Id).As("Panela.Id"))
.Select(Projections.Property(() => panela.IdcAtiva).As("Panela.IdcAtiva"))
.TransformUsing(Transformers.AliasToBean(typeof(PanelaCorrida)));
Don't know if is the best approach, but it worked. We will analyze best approaches using QueryOver/DetachedCriteria, but for now, this works great.
Note: I've removed some columns, just to explain how it worked.
Thanks again.

Compare object with an array with another array

I have a model Group:
public class GroupModel
{
[Key]
public int GroupModelId { get; set; }
[Required]
[MaxLength(50)]
[DataType(DataType.Text)]
public string GroupName { get; set; }
[Required]
public virtual ICollection<FocusArea> FocusAreas { get; set; }
...
And a model Focus:
public class FocusArea
{
public int FocusAreaId { get; set; }
public FocusEnum Focus { get; set; }
public List<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<GroupModel> GroupModel { get; set; }
public enum FocusEnum
{
Psych,
Medical,
LivingWith
}
Group and Focus has a many-to-many relationship. My Controller is receiving:
public ActionResult GroupSearch(string[] focusSelected) // Values possible are Pysch, Medical and LivingWith
{
List<GroupModel> groups;
...
Problem: I want to select the groups that have all the focus that are inside the focusSelected array.
What I've tried:
groups = groups.Where(t => t.FocusAreas.Where(x => focusSelected.Contains(x.Focus))).ToList()).ToList();
Obviously not working. Does anyone have another idea?
This may help you
var result = groups.Where(g => g.FocusAreas.All(f => focusSelected
.Any(fs => (FocusEnum)Enum.Parse(typeof(FocusEnum), fs, true) == f.Focus)));
Where needs a delegate / expression that returns bool. In your sample - you are putting Where inside Where, where Where returns collection.
Changing inner Where to All should do the trick:
var allSelParsed = focusSelected.Select(s => (FocusEnum)Enum.Parse(typeof(FocusEnum), s)
.ToList();
groups = groups.Where(gr => allSelParsed.All(selected =>
gr.FocusAreas.Any(fc =>
fc.Focus == selected)))
.ToList();
This should give you expected result
var result = groups.Where(g =>
focusSelected.All(fs =>
g.FocusAreas.Any(fa => fa.ToString() == fs)));

Why in many-to-one direction in relations (C# EF) i always get empty/null data but in oposite direction it's works fine?

Example:
public Tim getTim(int userID)
{
TimUsers timUser = _ctx.TimUser.FirstOrDefault(c => c.ID == userID);
return _ctx.Times.FirstOrDefault(c => c.ID == timUser.tim.ID);
}
and model (code first) sample:
public class TimUsers
{
public int ID { get; set; }
...
public virtual Tim time {get;set;}
}
public class Tim
{
public int ID { get; set; }
...
public virtual ICollection<TimeUsers> timeUsers { get; set; }
}
I'm 100% sure that a relation between TimeUser and Tim exist becouse this method give me a list of timeUser:
public IQueryable<TimeUsers> GetTimeUsersByTime(int TimeId)
{
return _ctx.TimeUser
.Where(c => c.tim.ID == TimeId)
.AsQueryable();
}
I'm sorry for syntax error but I wrote this sample without debbuging. Thanks for help.
You need to include the child:
var timeWithChildren = _ctx.Times.Include(t => t.time).FirstOrDefault(c => c.ID == timUser.tim.ID);
https://msdn.microsoft.com/en-us/data/jj574232.aspx

Fluent NHibernate "Could not resolve property"

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.

Categories

Resources