NHibernate mapping Dictionary by code - nhibernate

How to map this Dictionary using mapping-by-code:
public class User
{
public virtual Dictionary<Option, bool> Options { get; set; } }
}
Database looks like this:
User UserOptions Option
--- --- ---
Id Id Id
UserID
OptionID
bool_column
I tried that mapping (looking here):
Map(x => x.Dictionary,
m =>
{
m.Key(k => k.Column("UserID"));
m.Table("UserOptions");
},
k => k.ManyToMany(m =>
{
m.Column("OptionID");
}),
v => v.Element(m =>
{
m.Column("bool_column");
})
);
But there is an error:
An association from the table UserOptions refers to an unmapped class: System.Boolean

OK, it is bug.
Solved by using xml-mapping for User, and adding configuration.AddXmlFile("Mappings/User.hbm.xml");

Related

FluentNhibernate Map ValueObject

I have the following use case:
public class Object {
long Id
...
ISet<Tags> tags
}
public class Tag : IEquatable<Tag> {
string Label;
}
Object is an Aggregate Root and Tag a Value Object.
Both are stored in 2 different tables:
CREATE TABLE incident(id bigint, ...)
CREATE Table tag (object_id bigint References object(id), label varchar,...)
I'm trying to create the ClassMap using FluentNhibernate, which works well for object but I couldn't find a way to map it with Tag
public ObjectsMapping()
{
Id(x => x.Id).GeneratedBy.Assigned();
Version(x => x.ObjectVersion).Column("object_version");
HasMany(x => x.Tags).Inverse().Cascade.All().KeyColumn("object_id");
}
public TagsMapping()
{
CompositeId().KeyProperty(x => x.Label).KeyProperty(x => x.CreationTimestamp);
Map(x => x.Label);
Map(x => x.CreationTimestamp);
}
Any idea how to map that an entity that has a oneToMany relation with a ValueObject from another table ?
Basically I'm looking for an equivalient of Set() in NHibernate
Thank you
I found the solution:
In ObjectMapping:
HasMany(x => x.Tags).Component(x => { x.Map(k => k.Label); }).Table("tag");

Joining 3 tables and using a left outer join with linq in EF Core 3.1.1

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

NHibernate, child entities are not lazy loaded

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.

Unable to resolve property: Id

I'm getting the following error message:
NHibernate.HibernateException: NHibernate.HibernateException: Unable to resolve property: Id.
This error is thrown from the following line of code:
User userFound = session.QueryOver<User>()
.Where(x => x.Id == testObjects.TestUser.Id)
.SingleOrDefault();
My abbreviated mappings are as follows:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("USER_HEADER");
Id(x => x.Id, "USER_ID")
.GeneratedBy.Foreign("UserLocation");
HasOne(x => x.UserLocation)
.PropertyRef(x => x.Id)
.Cascade.All();
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Table("LOC_HEADER");
Id(x => x.Id, "LOC_ID");
HasOne(x => x.User)
.PropertyRef(x => x.Id);
}
}
I was able to query a User object before I added this relationship to Location so I know it has something to do with it but I'm not sure what exactly. I can successfully create a User object that is tied to a Location but cannot query it. Using ISession.Get produces the same error as the above QueryOver statement.
Below is the overall unit test I am running that is failing:
public void Can_Create_User()
{
using (NHibernate.ISession session = SessionFactory.GetCurrentSession())
{
using (NHibernate.ITransaction tran = session.BeginTransaction())
{
session.Save(testObjects.TestValidationDetail);
session.Save(testObjects.TestUser);
tran.Commit();
}
}
using (NHibernate.ISession session = SessionFactory.GetCurrentSession())
{
User userFound = session.QueryOver<User>().Where(x => x.Id == testObjects.TestUser.Id).SingleOrDefault();
Assert.IsNotNull(userFound);
Assert.AreEqual(userFound.Id, userFound.UserLocation.Id);
}
}
It turns out this was caused by me incorrectly using PropertyRef. In my instance I did not need to use this. The error was being generated because there was no property named Id but there was an ID named Id. I corrected my issues by changing my mappings to:
HasOne(x => x.UserLocation)
.PropertyRef(x => x.Id)
.Cascade.All();
to
HasOne(x => x.UserLocation)
.Cascade.All();
and
HasOne(x => x.User)
.PropertyRef(x => x.Id);
to
HasOne(x => x.User)
PropertyRef maps to property-ref is a legacy feature, it is meant to allow you to create many-to-one associations when the association is not done on the primary key of the association.
I am guessing you want to specify on what property the join is to be made and that is why you used PropertyRef.. if you are using Nhibernates default convention in the mapping for the Id of UserLocation you dont need to explicitly specify the property.. if you are explicitly giving the column name then you need to do the same here, but in that case you need to specify the exact same column name.
Hope that helps..

nhibernate inner join a table without a property in class

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