Fluent HNibernate Mapping Property Issue - nhibernate

i have two entities one called User and another called Membership which has a one to many mapping from User to Membership. I need to add a property on my User entity called CurrentMembership which gets the latest Membership row (ordered by the property DateAdded on the Membership Entity). I'd appreciate it if someone could show me how this can be done.
Thanks

I don't think the property needs to be mapped with Fluent NHibernate unless you are planning on storing it in the database, which doesn't necessarily sound like a good idea to me. The following code is more than likely all you need:
public class User
{
private IList<Membership> _Membership = new List<Membership>();
public IList<Membership> Memberships
{
get { return _Membership; }
}
public Membership CurrentMembership
{
get
{
return Memberships
.OrderByDescending(x => x.DateAdded).FirstOrDefault();
}
}
}

Related

EF Core 2.0 Trouble 'Cascading' Inserts for Related Entities When Updating Principle Entity

ASP.NET Core 2 Web application using a REST API. Currently using sqlite3 for development database. (Also tried migrating to SQL Server and got same results as below).
I'm sending an entity to web client, the client makes changes to the entity that involve adding a new related entity and then that updated principle entity gets sent back as json in body of PUT a request.
I was hoping the new related entity would get created automatically, but this is not happening. The simple properties on the principle entity are updated properly, but not reference properties. I'm not getting any exceptions or anything - it just seems to be ignoring the reference properties.
Simplified Classes (I removed other properties that shouldn't affect the relationship):
public partial class DashboardItem {
public int Id { get; set; }
public int? DataObjectId { get; set; }
public DataObject DataObject { get; set; }
}
public partial class DataObject {
public int Id { get; set; }
}
Portion of DbContext Fluent API for associated property:
modelBuilder.Entity<DashboardItem>(entity => {
entity.HasOne(p => p.DataObject)
.WithMany()
.HasForeignKey(p => p.DataObjectId);
});
Controller Method for PUT:
[HttpPut("{id}")]
public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != entity.Id)
{
return BadRequest();
}
_context.Entry(entity).State = EntityState.Modified;
try{
await _context.SaveChangesAsync();
}catch (DbUpdateConcurrencyException)
{
if (!DashboardItemExists(id)){
return NotFound();
}else {
throw;
}
}
return NoContent();
}
The simplified json (without all the other properties) would look like this (I've tried different variations of have the foreign key "DataObjectId" removed from the json, set to null, or set to zero in case that might be interferring.):
{
Id:1,
DataObjectId:null,
DataObject:{
Id: 0
}
}
When debugging in the controller action method, the existing "DashboardItem" principle entity created from the request body has the reference property "DataObject" populated before getting added to the DbContext, but the new DataObject never gets created in the database. There is only a SQL UPDATE statement issued for DashboardItem and no INSERT for DataObject.
I've also tried making the controller method synchronous instead of async, using DbContext.SaveChanges() instead of .SaveChangesAsync(), since there used to be a problem with that in earlier versions of EF Core related to creating related entities, even though I'm using 2.0 which already has a fix for that. Same result.
This EFCore Doc sounds like it should just work out of the box.
This has worked for me in a prior project. What am I missing here?
Basically, my mistake was in assuming the process of updating data was much simpler than it actually is when sending the updated data from a client in a web application.
After digging a lot more, it seems that the following line in my controller method for handling the PUT request is the problem:
_context.Entry(entity).State = EntityState.Modified;
Setting the entity entry state to Modified in this way results in Entity Framework Core ignoring the reference properties for the related objects - the SQL UPDATE generated will only address the columns in the entity table.
This simple summary eventually got me started down the right path.
Summarizing what I've now learned:
This controller method is dealing with a 'detached' entity that was edited and sent back from the client. The DbContext is not yet tracking this entity since I get a new instance of the context with each http request (hence the entity is considered 'detached'). Because it is not being tracked yet, when it is added to the DbContext, the context needs to be told whether this entity has been changed and how to treat it.
There are several ways to tell the DbContext how to handle the detached entity. Among those:
(1) setting the entity state to EntityState.Modified will result in ALL properties being included in the SQL update (whether they've actually changed or not), EXCEPT for the reference properties for related entities:
_context.Entry(entity).State = EntityState.Modified;
(2) adding the entity with a call to DbContext.Update will do the same as above, but will include the reference properties, also include ALL properties on those entities in the update, whether they've changed or not:
_context.Update(entity)
Approach #2 got things working for me, where I was just trying to get the new related child entity to be created in the Update to its parent.
Beyond that, DbContext.Attach() and DbContext.TrackGraph sound like thy provide more find-grained control over specifying what specific properties or related entities to include in the update.

Gii doesn't recognize many-to-many relationship to itself?

So I am trying to implement a friendlist, the above is the SQL diagram I made for my simple project and after generating the Models. I realized there was something wrong with the way Gii generated the model.
I wanted to make a many-to-many relationship with User to itself, but this is what I got:
class User {
...
public function getPosts()
{
return $this->hasMany(Post::className(), ['userId' => 'id']);
}
}
class Friend {
...
public function getFriend()
{
return $this->hasOne(Member::className(), ['id' => 'friendId']);
}
}
The User class doesn't have any relationship with itself, I expected something like getUsers() inside of User, but it didn't generate it. I initially thought about not making a model with the junction table, but I did so just to see what would happen. I don't think I need it. So I am not sure how to do this correctly? Do I need to get rid of my Junction Table Models and Do I need to make the relationship between User to itself and User to Message manually? I thought about doing a many-to-many in User and Message and a many-to-many in User for User. Is this the right thing? Tell me if I am wrong. Thank you.
You are on a true way. You need a junction table for implementing your goal. Easily as you done this, you must define two model: User and Friend. Now on your User model at first you must define a relation for get the list of all friends, Suppose call it getFriendsLists:
public function getFriendsLists()
{
return $this->hasMany(Friend::className(), ['userId' => 'id']);
}
This relation says that "Get me all account that are connected with me, i.e. if my id is 102, this relation return all record of friend table that their userIds are 102". Well, now we get all friends with a relation on User model, let call him getFriends:
public function getFriends()
{
return $this->hasMany(User::className(), ['friendId' => 'id']
->via('friendsList');
}
Notice that 'friendsList' as is a parameter of via method, is our predefined relation on top of this answer. Now easily you can get all account that are friends of our example (User with id 102):
public FriendController extends Controller
{
// Some code goes here!
public function actionFriendList($id)
{
$user = User::findOne($id);
$friends = $user->friends;
return $this->render('friend-list', ['friendsArray' => $friends]);
}
}
And use them on your friend-list view file as $friendsArray variable. Extra note that $user->friends use friends relation that you defined on User model with getFriends method.

Fluent nHibernate SubclassMap and AddFromAssemblyOf

I created a generic user repository base class that provides reusable user management functionality.
public class UserRepository<TUser> where TUser : new, IUser
{
}
I have a concrete implementation of IUser called UserImpl, and corresponding mapping class UserImplMap : ClassMap<UserImpl> (they all are in the same namespace and assembly). I add the mapping using AddFromAssemblyOf . I also use this to create / generate the schema.
So far so good and things work as expected.
Now, in a different project, I needed a few additional properties in my IUser implementation class, so I implemented a new class UserImplEx : UserImpl. This class has the additional properties that I needed. Also, I created a new mapping class UserImplExMap : SubclassMap<UserImplEx>
Now when I create schema using this approach, I get two tables one for UserImpl and one for UserImplEx.
Is is possible to configure / code Fluent mapping in some way so that all the properties (self, plus inherited) of UserImplEx get mapped in a single table UserImplEx instead of getting split into two tables?
Alternatively, if I provide full mapping in UserImplExMap : ClassMap<UserImplEx>, then I do get the schema as desired, but I also get an additional table for UserImpl (because corresponding mapping is present in the UserRepository assembly). If I follow this approach, is there a way to tell AddFromAssemblyOf to exclude specific mapping classes?
Option 1
since you have inhertance here and want the correct type back NH has to store the type somewhere, either through the table the data is in or a discriminator.
If a discriminator column in the table does not matter then add DiscriminatorColumn("userType", "user"); in UserImplMap and DiscriminatorValue("userEx") in UserImplExMap
Option 2
class MyTypeSource : ITypeSource
{
private ITypeSource _inner = new AssemblyTypeSource(typeof(UserImplMap).Assembly);
public IEnumerable<Type> GetTypes()
{
return _inner.Where(t => t != typeof(UserImplMap)).Concat(new [] { typeof(UserImplExMap) });
}
public void LogSource(IDiagnosticLogger logger)
{
_inner.LogSource(logger);
}
public string GetIdentifier()
{
return _inner.GetIdentifier();
}
}
and when configuring
.Mappings(m =>
{
var model = new PersistenceModel();
PersistenceModel.AddMappingsFromSource(new MyTypeSource());
m.UsePersistenceModel(model);
})

NHibernate create object from only collection of children

I have a table logging web page hits. Something like: {VisitId, VisitorId, Url, Date}
(The visitor ID is a GUID stored in a cookie)
I would like to create a Visitor object that has a collection of Visit objects.
class Visitor {
public virtual Guid VisitorId { get; set; }
public virtual IList<Visit> Visits { get; set; }
}
Rather than add another table for Visitor, can NHibernate create this object just from the collection of Visits?
Ideally, I would like to write:
var visitor = session.Get<Visitor>(guidFromCookie)
And then be able to work with the Visits list and persist changes back to the DB.
(I'm using FluentNHibernate and NHibernate 3.0)
I'm new to NHibernate, but it seems the something should be possible using a custom IEntityPersister, or is this too low level and loads of work? Any suggestions would be appreciated.
When you say "create this object", do you mean retrieve? What is your reason for not having a visitor table? You could use the criteria API or hbm to load a list of visits by the guid if you don't want a visitor entity/table.
If you mapped Visitor and made it Lazy Loaded, you might be able to do this. You'd have to tell NHibernate that the table existed, even though it didn't. However, when you want to get the Visitor object (note that the only property mapped is the Id), then instead of using .Get(), use .Load() which will return an uninitialized proxy. So you'll have an entity, but it won't actually hit the database, so it will never know that the table doesn't exist.
public class VisitorMap : ClassMap<Visitor>
{
public VisitorMap()
{
Table("SomeNonExistentTable");
LazyLoad(); // should be the default anyway
Id(x => x.Id)
.GeneratedBy.Guid();
HasMany(x => x.Visits)
.AsList()
.Not.LazyLoad();
}
}
...and then...
var visitor = session.Load<Visitor>(guidFromCookie);
foreach(var visit in visitor.Visits)
{
// do wonderful things
}

NHibernate - Do I have to have a class to interface with a table?

I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.