NHibernate: Populate new entities relative properties with foreign key ID - nhibernate

I have structured my NHibernate models to hide the foreign key IDs as seems to be best practice, so instead of having both the relationship and the key modeled like this:
public class CompanyOffice
{
public virtual int Id { get; set; }
public virtual int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
I simply have:
public class CompanyOffice
{
public virtual int Id { get; set; }
public virtual Company Company { get; set; }
}
And let NHibernate take car of the rest. I like this, but I have a few tables with multiple foreign keys that need setting and when I create a new entity, in an MVC action, I'm having to do this:
public ActionResult CreateNewThing(int stuffId, int whatId, int blahId)
{
var stuff = _service.GetStuffById(stuffId);
var what = _service.GetWhatById(whatId);
var blah = _service.GetBlahById(blahId);
var thing = new Thing { Stuff = stuff, What = what, Blah = blah };
}
Which means three trips to the database to get the entities, when I don't really need them as I already have the IDs and could have just done:
var thing = new Thing { StuffId = stuffId, WhatId = whatId, BlahId = blahId };
Is there another way to achieve what I'm trying to do without hitting the database for the entities?
Should I just bite the bullet and map the foreign keys as well as the relationships?
Or am I being to concerned about the database trips and should I just crack on?

You are right to avoid mapping the FK Id, to avoid 3 addtional trips do this:-
var thing = new Thing {
Stuff = session.Load<Stuff>(20),
What = session.Load<What>(21),
Blah = session.Load<Blah>(2234),
}
session.Load is slightly different to session.Get
Also it is worth noting that Thing.Stuff.Id also does not hit the database.
This is a great post, I quote:-
In session.load(), Hibernate will not hit the database (no select
statement in output) to retrieve the Stock object, it will return a
Stock proxy object – a fake object with given identify value. In this
scenario, a proxy object is enough for to save a stock transaction
record.

Related

Entity Framework Core - manually assigning id's to linked entities

I've reviewed other related questions on StackOverflow, but I haven't found one that matches my exact question or scenario.
I have an JavaScript front-end where I have two entities that are linked, so in the front-end I have JSON objects that get 'linked' to each other like this:
entity1
name = "something"
entity2Id = 0
entity2
1st record
id = 0
name = "something else"
2nd record
id = 1
name = "something else again"
I then send the JSON to a .NET back end, and the idea is that as I'm manually specifying the primary key for entity2 and setting that as the 'foreign key' reference in my entity1, Entity Framework then somehow needs to generate a valid id for entity2, and maintain a reference to that id in the entity2id property of entity1.
I suspect I may be getting this all horribly wrong and that there's a fairly standard approach to achieving what I'm trying to do. What should I do to achieve the desired result? (I'm using a SQLite database, which probably doesn't make a difference.)
You have a one-to-many relationship there. To make it easier to explain I'll change your Entit1 to Book and Entity2 to Genre. So something like this:
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Book> Books { get; set; }
}
You can then use the FluentApi to configure the relationship (most importantly the FK key on Books from Genres
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>()
.HasOne(p => p.Genre)
.WithMany(b => b.Books)
.HasForeignKey(p => p.GenreId);
}
Now we can achieve your two scenarios:
1 - Create a new Book along with a new Genre
2 - Create a new Book that uses an existing Genre on db
var bookWithNewGenre = new Book
{
Name = "Book 1",
// Here we are creating a new Genre, without Id.
// GenreId here will have the default value of 0,
// which EF will use to find out that it has to be created
Genre = new Genre { Name = "Mistery"}
};
var bookWithExistingFictionGenre = new Book
{
Name = "Book 2",
// Here we are specifying the GenreId = 1 which already exists in the Database
GenreId = 1,
// You don't need to set this to null
// but I like doing it to make EF and my code clear that I'm using an existing Genre
Genre = null
};
using (var context = new BookContext())
{
context.Books.Add(bookWithNewGenre);
context.Books.Add(bookWithExistingFictionGenre);
await context.SaveChangesAsync();
}
After saving you'll have this in the database:
You'll probably have to change your front-end to start sending also the Genre object along. In cases where it's a new one, the Id will be missing. When you serialize it into your c# types, you can figure it out if you have to create new instance or just maybe check if the Id front-end passed exists or not.
Remarks: I did all this using Sqlite and the EF core packages version 2.2.0-preview1-35029.

Best Practice with MVC4 and EF5 to apply changes

I have a CustomerOrder-view where I would like to change an existing CustomerOrder.
I have a viewmodel that very simpliefied looks something like this:
public class CustomerOrderViewModel
{
public int ID { get; set; }
public Customer Customer { get; set; }
public DateTime Date { get; set; }
public IEnumerable<OrderRow> OrderRows { get; set; }
}
public class OrderRow
{
public int id { get; set; }
public int price { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
I also have a database with mapping tables / fields.
In my GET Action Method I load the Order with the help of Automapper like this:
var customerOrder = using (var ctx = new My_Entities()) {
return ctx.CustomerOrders.
Include("Orderrows").
Include("Customer").
Single(o => o.CustomerOrderID == id);
}
var model= AutoMapper.Mapper.DynamicMap<DataAccessLayer.CustomerOrder, CustomerOrderViewModel>(customerOrder);
In the View I use Knockout to bind to a viewmodel there, where the user can update the CustomerOrder. That includes editing Customer information and adding new orderrows etc.
Then in the post back a map the ViewModel back to the ObjectContext CustomerOrder:
var customerOrderToBeSaved =
AutoMapper.Mapper.DynamicMap<CustomerOrderViewModel, CustomerOrder>(
customerOrderViewModel);
try
{
using (var ctx = new MyEntities())
{
ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.CustomerOrders.ApplyCurrentValues(customerOrderToBeSaved);
...
ctx.SaveChanges();
}
}
I get the error message: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
OK, that I can understand. But how should I go about this? Can I get the existing object and apply Changes to that one, because that is really what I'd like. I've tried to look up the old one and detach it but I haven't got it to wrok.Perhaps I'm doing this in a completely wrong way. Please advice.
You should not attach customerOrderToBeSaved, see MSDN about the argument of ApplyCurrentValues.
The detached object that has property updates to apply to the original object.
So you've got to load the entity from the database into the context and then ApplyCurrentValues with the detached object that has the new values.
You don't have to load the row from the database to update it.
You can do something like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.Entry( entity ).State = EntityState.Modified;
ctx.SaveChanges();
This will tell EF to issue an UPDATE SQL statement that overwrites all the columns in the record.
You can select which columns you want to update like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
var entry = ctx.Entry( entity );
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
...
ctx.SaveChanges();
If you do this, EF will only include the columns you've marked as modified in the UPDATE statement.

Supersedes clause in database structure

Imagine a database table that looks like this:
create table [dbo].[user]
(
id int IDENTITY(1,1),
username varchar(50) NOT NULL,
firstname varchar(20) NOT NULL,
lastname varchar(30) NOT NULL,
currentid int NULL,
processedby varchar(50) NOT NULL,
processeddate varchar(50) NOT NULL
processedaction varchar(50) NOT NULL
)
What I want to do is to setup NHibernate to load it into my user object, but I only want the current version of the object "user" to be brought back. I know how to do a SQL select to do this on my own, and I feel as if there's something in nHibernate with the usage of triggers and event listeners, but can anyone tell me how to implement the nHibernate repository so I can:
{Repository}.GetCurrent(id) <- pass it any of the ids that are assigned to any of the historical or the current record, and get back the current object.
{Repository}.Save(user) <- I want to always insert the changes to a new row, and then update the old versions to link back to the new id.
Edit
So, there's some confusion here, and maybe I explained it wrong... What I'm trying to do is this, in regards to always getting the current record back...
Select uc.*
FROM User uo
JOIN User uc on uo.currentid=uc.id
WHERE uo.id==:id
But, I don't want to expose "CurrentID" to my object model, since it has no bearing on the rest of the system, IMHO. In the above SQL statement, uo is considered the "original" object set, and uc is considered the current object in the system.
Edit #2:
Looking at this as a possible solution.
http://ayende.com/blog/4196/append-only-models-with-nhibernate
I'm honestly being pigheaded, as I'm thinking about this backward. In this way of running a database, the autoincrementing field should be the version field, and the "id" field should be whatever the autoincrementer's value has at the time of the initial insert.
Answer:
I don't want to take #Firo's fury, and I'm not going to remove it from him, as he took me down the right path... what I wound up with was:
Created a base generic class with two types given
a. type of the object's "ID"
b. type of the object itself.
instantiate all classes.
create a generic interface IRepository class with a type of the object to store/retrieve.
create an abstract generic class with a type of the object to store/retrieve.
create a concrete implementation class for each type to store/retrieve.
inside of the create/update, the procedure looks like:
Type Commit(Type item)
{
var clone = item.DeepClone();
_Session.Evict(item);
clone.Id = 0;
clone.ProcessedDate = DateTime.Now;
if (clone.Action.HasValue)
{
if (clone.Action == ProcessedAction.Create)
clone.Action = ProcessedAction.Update;
}
else
{
clone.Action = ProcessedAction.Create;
}
clone.ProcessedBy = UserRepos.Where(u => u.Username == System.Threading.Thread.CurrentPrincipal.Identity.Name).First().Current;
var savedItem = (_Session.Merge(clone) as Type);
_Session.CreateQuery("UPDATE Type SET CurrentID = :newID where ID=:newID OR CurrentID=:oldID")
.SetParameter("newID", savedItem.Id)
.SetParameter("oldID", item.Id)
.ExecuteUpdate();
return savedItem;
}
In the delete method, we simply update the {object}.Action = ProcessedAction.Delete
I wanted to do this another way, but realizing we need to eventually do historical comparisons, we weren't able to ask nHibernate to filter the deleted objects, as the users will want to see that. We'll create a business facade to take care of the deleted records.
Again, much thanks to #Firo for his help with this.
So, with all that, I can finally do this:
var result = {Repository}.Where(obj => obj.Id == {objectID from caller}).FirstOrDefault();
if (result != null)
{
return result.Current;
}
else
{
return null;
}
and always get my current object back for any requesting ID. Hope it helps someone that is in my situation.
in mapping if you use FluentNHibernate
public UserMap : ClassMap<User>
{
public UserMap()
{
Where("id = currentid"); // always bring back the most recent
}
}
// in Userrepository
public void Update(User user)
{
var clone = user.Clone();
session.Evict(user); // to prevent flushing the changes
var newId = session.Save(clone);
session.CreateQuery("UPDATE User u SET u.currentid = :current") // <-- hql
.SetParameter("current", newId)
.ExecuteUpdate();
}
objectgraphs are a lot trickier with this simple code. I would then do one of the following:
use NHibernate.Envers to store auditing information for me
explicitly creating new entities in BL code
i once saw an append-only-model doing something like the following
// UserBase is there to ensure that all others referencing the User doesnt have to update because user properties changed
class UserBase
{
public virtual int Id { get; set; }
public virtual ICollection<PersonDetails> AllDetails { get; private set; }
public virtual PersonDetails CurrentDetails
{
get { return _currentDetauils; }
set { _currentDetauils = value; AllDetails.Add(value); }
}
// same as above
public virtual ICollection<ConfigDetails> AllConfigs { get; set; }
}
class Order
{
public virtual int Id { get; set; }
public virtual UserBase User { get; set; }
public virtual IList<OrderDetail> AllDetails { get; private set; }
public virtual IList<OrderDetail> ActiveDetails { get; private set; }
public virtual void Add(OrderDetail detail)
{
AllDetails.Add(detail);
ActiveDetails.Add(detail);
}
public virtual void Delete(OrderDetail detail)
{
detail.Active = false;
ActiveDetails.Remove(detail);
}
}
class OrderDetail
{
public virtual int Id { get; set; }
public virtual Order Parent { get; set; }
public virtual bool Active { get; set; }
}
class OrderMap : ClassMap<Order>
{
public OrderMap()
{
HasMany(o => o.AllDetails);
HasMany(o => o.ActiveDetails).Where("active=1");
}
}
// somewhere
public void UpdateTaxCharge(OrderDetail detail, TaxCharge charge)
{
var clone = detail.Clone();
clone.TaxCharge = charge;
detail.Order.Delete(detail);
detail.Order.Add(clone);
}
You can tell NHibernate what exactly SQL it should generate when persisting and loading an entity. For example you can tell NHibernate to use a stored procedure instead of a plain SQL statement. If this is an option for you I can farther elaborate my answer.

How to auto-load details (with conditions) associated with an entity using Ria Services?

I'm developing a project using Silverlight 4 and Entity Framework 4 and I'm trying to auto-load the details (with conditions) associated with an entity when the client loads the EntityQuery.
So far, I've been able to put in place a solution, using the Include attribute, that returns all the details associated with the master entity. What I'm missing here is to be able to filter out the details based on some criteria.
As an example, here's what my entities look like:
Entity Movie
Id (int)
[Include]
MovieLocalizedInformations (EntityCollection<MovieLocalizedInformation>)
Entity MovieLocalizedInformation
Id (int)
Movie_Id (int)
LanguageCode (eg.: en)
Title
On my DomainService object, I expose the following method:
public IQueryable<Movie> GetMovies( string languageCode )
{
return this.ObjectContext.Movies.Include( "MovieLocalizedInformations" );
}
This works fine. But when I try to add where clause to filter out the localized information based on the language code, only the movies get loaded on the client.
Is there a way to achieve the filtering in one query?
Note: I'm also using the DomainDataSource with paging on the client so the solution needs to work with that.
Any help would be greatly appreciated!
Thanks,
Jacques.
Not sure about Enitity Framework but with a LinqToSqlDomainService you use the LoadWith loadOption
to include the details entities and then use the AssociateWith LoadOption to filter the detail e.g
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Movies>(i => i.MovieLocalizedInformations);
options.AssociateWith<Movies>(i => i.MovieLocalizedInformations.Where(d=> myListOfIds.Contains(d.LocationId)));
Ok,
For efficiency reason, I decided to go with custom DTO object that fetches the localized information and flatten the result.
But, the same problem occurred when my custom DTO needed to reference another custom localized DTO.
Here is how I came to do the same as the .Include( "PropertyName" ) that the ObjectSet offers:
Entity LocalizedMovieCollection
public class LocalizedMovieCollection
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
[Include]
[Association( "LocalizedMovieCollection_LocalizedMovies", "Id", "MovieCollection_Id" )]
public IEnumerable<LocalizedMovie> Movies { get; set; }
}
Entity LocalizedMovie
public class LocalizedMovie
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
public int MovieCollection_Id { get; set; }
[Include]
[Association( "LocalizedMovie_LocalizedMovieCollection", "MovieCollection_Id", "Id", IsForeignKey = true]
public LocalizedMovieCollection MovieCollection { get; set; }
}
Then, I've declared two methods: One that returns an IQueryable of LocalizedMovieCollection and the other, an IQueryable of LocalizedMovie. (Note: There must be at least one method that returns each type of entity for the entity to get auto-generated on the Silverlight client)
My goal is to automatically load the MovieCollection associated with a Movie so the method definition to get the movies is as follow:
public IQueryable<LocalizedMovie> GetMovies( string languageCode )
{
return from movie in this.ObjectContext.Movies
join movieLocalizedInfo in this.ObjectContext.MovieLocalizedInformations
on movie equals movieLocalizedInfo.Movie
join movieCollection in this.ObjectContext.MovieCollections
on movie.MovieCollection equals movieCollection
join movieCollectionLocalizedInfo in this.ObjectContext.MovieCollectionLocalizedInformations
on movieCollection equals movieCollectionLocalizedInfo.MovieCollection
where movieLocalizedInfo.LanguageCode == languageCode && movieCollectionLocalizedInfo.LanguageCode == languageCode
select new LocalizedMovie()
{
Id = movie.Id,
Name = movieLocalizedInfo.Name
MovieCollection_Id = movieCollection.Id,
MovieCollection = new LocalizedMovieCollection(){ Id = movieCollection.Id, Name = movieCollectionLocalizedInfo.Name }
}
}
When the Silverlight client loads the query, all the LocalizedMovies and their associated LocalizedMovieCollections will be loaded into the context.

NHibernate - How to write this Query: Select parents & find child for each parent that matches a condition

OK, first my simple Domain Model is 2 classes with a one-to-many relationship, a simple Parent -> child relationship. A 'Tweet' has one or more 'Votes', but each Vote belongs to just one Tweets etc.
public class Tweet
{
public virtual long Id { get; set; }
public virtual string Username { get; set; }
public virtual string Message { get; set; }
public virtual ISet<Vote> Votes { get; set; }
}
public class Vote
{
public virtual long Id { get; set; }
public virtual long TwitterUserId { get; set; }
public virtual DateTime VotedDate { get; set; }
public virtual Tweet Tweet { get; set; }
}
I'm trying to write a query in either HQL, ICriteria or NHibernate LINQ, that selects all Tweets, but also add two columns that selects:
A count of the number of votes, and...
Whether a particular user has voted for that tweet, based on a particular TwitterUserId
With the two extra columns, I'm not expecting to get Tweet domain object back, and would probably need to run a Report query, that's OK. But I'm struggling to figure out how to write this query.
I know how to write this as a Stored Procedure, or using LINQ 2 SQL. If it helps I will express this as a LINQ to SQL query.
long userId = 123;
var tweets = from t in dataContext.Tweets
where t.Application == app
orderby t.PostedDate desc
select new TweetReport()
{
Id = t.Id,
Username = t.Username,
Message = t.Message,
TotalVotes = t.Votes.Count(),
HasVoted = t.Votes.Any(v => v.TwitterUserId == userId)
};
I know this will work in LINQ 2 SQL, and generate reasonably efficient T-SQL, but I can't figure out how to write this in NHibernate.
Update: I tried running the above LINQ query in NHibernate by using the NHibernate LINQ provider built for NHibernate v2, eg:
var tweets = from t in Session.Linq<Tweet>()
where (snip)
But it didn't work. Has LINQ support in Nhibernate 3.0 improved? I'm a bit reluctant to use version 3.0 because it's still alpha, but if this will work, then I might give it a go.
Update 2: Thanks to Diego Mijelshon's suggestion, I upgraded to NHibernate 3.0 alpha 2 and wrote the query in LINQ:
var tweets = from t in Session.Query<Tweet>()
where t.App == app
orderby t.PostedDate descending
select t;
int totalRecords = tweets.Count();
var pagedTweets = (from t in tweets
select new TweetReport()
{
Id = t.Id,
TwitterId = t.TweetId,
Username = t.Username,
ProfileImageUrl = t.ImageUrl,
Message = t.Message,
DatePosted = t.PostedDate,
DeviceName = t.Device.Name,
DeviceUrl = t.Device.Url,
TotalVotes = t.Votes.Count(),
HasVoted = t.Votes.Any(v => v.TwitterUserId == userId)
})
.Skip(startIndex)
.Take(recordsPerPage)
.ToList();
return new PagedList<TweetReport>(pagedTweets,
recordsPerPage, pageNumber, totalRecords);
It's exactly the same with NHibernate 3; just replace dataContext.Tweets with session.Query<Tweet>.
Alternatively, you can create a context class that exposes session.Query<Tweet> as an IQueryable<Tweet> Tweets property, then the code would be 100% unchanged.