I have 3 tables, Notices, Users, and Likes. I want to get all notices with user name and information if user likes this notice.
So far I have this code, but it returns one notice multiple times (one for each like):
return context.notices
.GroupJoin(context.Users, notice => notice.CreatedBy, user => user.Id, (notice, users) => new { notice, users })
.SelectMany(group => group.users.DefaultIfEmpty(), (group, user) =>
new
{
group.notice,
user
})
.GroupJoin(context.Likes, noticeDto => noticeDto.notice.Id, like => like.ItemId, (noticeDto, likes) => new { noticeDto, likes })
.SelectMany(group => group.likes.DefaultIfEmpty(), (group, like) =>
new NoticeDto
{
CreatedByName = (group.noticeDto.user == null ? "" : group.noticeDto.user.FullName),
Id = group.noticeDto.notice.Id,
Liked = like.CreatedBy == userId,
});
I also tried this code.. but I am getting an error:
return context.notices
.GroupJoin(context.Users, notice => notice.CreatedBy, user => user.Id, (notice, users) => new { notice, users })
.SelectMany(group => group.users.DefaultIfEmpty(), (group, user) =>
new
{
group.notice,
user
})
.GroupJoin(context.Likes, noticeDto => noticeDto.notice.Id, like => like.ItemId, (noticeDto, likes) => new { noticeDto, likes })
.Select((group) =>
new NoticeDto
{
CreatedByName = (group.noticeDto.user == null ? "" : group.noticeDto.user.FullName),
Id = group.noticeDto.notice.Id,
Liked = group.likes != null ? group.likes.Any(w => w.CreatedBy == userId) : false,
});
This is the error I get:
Processing of the LINQ expression 'DbSet
.LeftJoin(
outer: DbSet
.AsQueryable(),
inner: notice => notice.CreatedBy,
outerKeySelector: user => user.Id,
innerKeySelector: (notice, user) => new {
notice = notice,
user = user
})
.GroupJoin(
outer: DbSet,
inner: noticeDto => noticeDto.notice.Id,
outerKeySelector: like => like.ItemId,
innerKeySelector: (noticeDto, likes) => new {
noticeDto = noticeDto,
likes = likes
})'
by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core.
Can anyone help me achieve what I need?.. Thank you.
Notice
public Guid Id { get; set; }
public Guid CreatedBy { get; set; }
User
public Guid Id { get; set; }
public string FullName{ get; set; }
Like
public Guid Id { get; set; }
public Guid CreatedBy { get; set; }
public Guid ItemId { get; set; }
Like's ItemId is Notice's Id? Notice's and Like's CreatedBy is User's Id?
If so, try this.
return context.notices.Include(x => x.Users)
.Include(x => x.Likes)
.Include("Likes.Users");
Database
Result
EDITED
As you do not have foreign key and relationship, then you can try this
var users = context.Users;
return context.notices.Select(x => new Notice()
{
Id = x.Id,
CreatedBy = x.CreatedBy,
Users = users.Where(y => y.Id == x.CreatedBy).FirstOrDefault(),
Likes = context.Likes.Where(y => y.ItemId == x.Id)
.Select(y => new Likes()
{
Id = y.Id,
CreatedBy = y.CreatedBy,
ItemId = y.ItemId,
Users = users.Where(z => z.Id == y.CreatedBy).FirstOrDefault()
}).ToList()
});
why are the children eager loaded ?
This is the relation :
public class Order
{
private IList<Product> _Product;
public virtual IReadOnlyCollection<Product> Products { get => _Product.ToList(); }
}
public class Product
{
private IList<Item> _Item;
protected internal virtual IReadOnlyCollection<Item> Items { get => _Item.ToList(); }
}
public class Item
{
private string _name;
public string Name { get => _name; }
private decimal _quantity;
public decimal Quantity { get => _quantity; }
}
Some method that gets the aggregate from DB
void Get(aggregateId)
{
var q = await Session.GetAsync<TAggregate>(aggregateId, cancellationToken);
var y = q as Order;
if (NHibernateUtil.IsInitialized(y.Products)) /// => TRUE
}
The Configurations for Order and Product
OrderConfiguration :
mapping
.HasMany<Product>(Reveal.Member<Order>("Products"))
.Access.LowerCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Not.KeyNullable()
.Not.KeyUpdate()
.LazyLoad();
ProductConfiguration
mapping
.HasMany<Item>(Reveal.Member<Product>("Items"))
.LazyLoad()
.Component(
composit =>
{
composit
.Map(instruction => instruction.Name)
.Column("Name")
.Not.Nullable();
composit
.Map(instruction => instruction.Quantity)
.Column("Quantity")
.Not.Nullable();
});
Why does NHibernate eager loads all the data instead of waiting for it to be loaded only if needed ?
How can this be made lazy ?
Your use of ToList() on the _Product and _Item collections is inadvertently triggering the lazy loading.
Though I was reading through the NHibernate Cookbook and all available forum-posts up and down, I'm still not able to get this simple query done:
I have users with everyone having one account. Each account hast a balance.
The classes look like that:
public class User
{
public virtual int Id { get; set; }
public virtual Account Account { get; set; }
public virtual bool Active { get; set; }
}
public class Account
{
public virtual int Id { get; set; }
public virtual double Balance { get; set; }
}
Now I would like to sum the balance of all active users. Nothing more...
In plain SQL it is quite easy:
SELECT SUM(a.Balance)
FROM User u
INNER JOIN Account a
ON u.Account_id = a.Id
WHERE u.Active = 'true'
I don't have any I idea, how I could solve that with the new QueryOver-Api from NHibernate 3. Could you please provide a code-example?
Thank you in advance!
Daniel Lang
EDIT
I know, that with NHibernate Linq it is very easy too, but I would like to solve it using QueryOver... Here is the working Linq-Example:
var result = Session.Query<User>()
.Where(x => x.Active)
.Sum(x => x.Account.Balance)
SOLUTION
Thanks to AlexCuse I could find the final solution (he was very very close) - here is the full code:
User userAlias = null;
Account accountAlias = null;
session.QueryOver<User>(() => userAlias)
.JoinAlias(() => userAlias.Account, () => accountAlias)
.Where(() => userAlias.Active)
.Select(Projections.Sum<Account>(acct => accountAlias.Balance))
.SingleOrDefault<double>()
Have you tried something like this?
session.QueryOver<User>(() => userAlias)
.JoinAlias(() => userAlias.Account, () => accountAlias)
.Where(() => userAlias.Active)
.Select(Projections.Sum<Account>(acct => acct.Balance))
.UnderlyingCriteria.UniqueResult()
I'm not sure what the UniqueResult equivalent is in the QueryOver API, so had to go through the underlying criteria.
You wrote in your answer:
User userAlias = null;
Account accountAlias = null;
session.QueryOver<User>(() => userAlias)
.JoinAlias(() => userAlias.Account, () => accountAlias)
.Where(() => userAlias.Active)
.Select(Projections.Sum<Account>(acct => accountAlias.Balance))
.SingleOrDefault<double>()
You don't need to have an alias for the generic type. It could be:
Account accountAlias = null;
session.QueryOver<User>()
.JoinAlias(user => user.Account, () => accountAlias)
.Where(user => user.Active)
.Select(Projections.Sum<Account>(acct => accountAlias.Balance))
.SingleOrDefault<double>()
i have the following model:
public class FlatMap : ClassMap<Flat>
{
public FlatMap()
{
Id(m => m.FlatID).GeneratedBy.Identity();
Map(m => m.Name);
Map(m => m.Notes);
Map(m => m.Released);
}
}
public class BuildingMap : ClassMap<Building>
{
public BuildingMap()
{
Id(i => i.BuildingID).GeneratedBy.Identity();
Map(m => m.Name);
HasMany<Flat>(m => m.Flats).Cascade.All().KeyColumn("BuildingID").Not.LazyLoad();
}
}
public class ContractMap : ClassMap<Contract>
{
public ContractMap()
{
Id(m => m.ContractID).GeneratedBy.Identity();
Map(m => m.Amount);
Map(m => m.BeginIn);
Map(m => m.EndIn);
References(m => m.RentedFlat);
}
}
how can i make the following query using fluent nhibernate ?
Select * From Contract
Inner Join Flat On Contract.RentedFlatID = Flat.ID
Inner Join Building On Building.BuildingID = Flat.BuildingID
Where Building.BuildingID = #p0
especially there no reference from Flat to Building?? and i don't want it to be !
of course the reference i am talking about in order to be able to do something like this
var criteria = session.CreateCriteria<Contract>().CreateCriteria ("RentedFlat").CreateCriteria ("Building"/*there is no such property in Flat class*/);
i solved a problem, but not the way as i think is good.
but i will make this as an answer until someone provide me a better solution.
i add a property BuildingID to the Flat class, and modified the mapping class to :
Map(m => m.BuildingID);
now i can do the following query:
criteria.CreateCriteria("RentedFlat")
.Add(Restrictions.Eq("BuildingID", selectedBuilding.BuildingID));
I'm a n00b. Here's what I want to do:
Use AutoMapping to configure every property between the model -> table. Then I would like to override 2 specific items in a fluent map. The two items are: Id & Table name.
So my Maps look like this:
public class BillMasterMap : ClassMap<BillMaster>
{
public BillMasterMap()
{
Table("BILLMAST");
Id(x => x.SYSKEY);
}
}
And my factory settings look like this:
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Where(x => x.Namespace.EndsWith("Entities"))))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<BillInvoiceMap>())
.BuildSessionFactory();
}
return _sessionFactory;
}
The problem is that FNH finds the Id override for BillMaster, but not for BillInvoice which looks like this (identical it seems)
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Id(x => x.SYSKEY);
Table("BILLINV");
}
}
I've gotten around the problem by configuring my automapping with the Setup() as shown below:
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities"))))
But I would like to combine auto & fluent as other tables don't use "SYSKEY" as their Id column.
Thoughts? Is this a FNH bug?
Got it working.
1.) I have to setup AutoMapping with the Setup method I described above
2.) Additionally I have to setup fluentmappings with the Id method
when both are setup like this, then it works for me.
So
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(
c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<BaseEntity>();
m.AutoMappings.Add(AutoMap.AssemblyOf<BaseEntity>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities")));
})
.BuildSessionFactory();
}
return _sessionFactory;
}
And the map
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Table("BILLINV");
Id(x => x.SYSKEY);
}
}