Does NHibernate always generate update for all columns?
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Address { get; set; }
}
Person p = Session.Load(1);
p.Name = "New Name";
Session.Flush();//Update for all columns, but I change only Name
Is it normal behavior for NHibernate or my mistake? I use Fluent NHibernate and AutoMapping.
That is the default behavior, but you can make NH update modified columns only by adding dynamic-update="true" to your <class> mapping.
NHibernate always updates all mapped columns. This should be no trouble if the other columns didn't change, since on update the data has been previously pumped from the underlying datastore, so basically, it only reset the column values to their own orginal values. No problem about it.
If you want to override this behaviour, you need to implement the IInterceptor interface.
Related
I have a Web API that uses entity framework. I have several tables there were created using the code first setup. My Competitions class is defined below.
Everything works great and I'm able to get my Competitions table data along with all the data in the navigation properties that are returning a collection. However, I'm not able to get any values for the CompetitionTypes and Users navigation properties. OwnerId references UserId in the Users table.
How would I get the linked data in my CompetitionTypes and Users table? I basically want the same thing as the three collection navigation properties, except that CompetitionTypes and Users would only return one row.
public partial class Competitions
{
[Key, Required]
public int CompetitionId { get; set; }
public int CompetitionTypeId { get; set; }
public int OwnerId { get; set; }
public string CompetitionName { get; set; }
public CompetitionTypes CompetitionTypeId { get; set; }
public Users UserId { get; set; }
public ICollection<Participants> Participants { get; set; }
public ICollection<ResultStats> ResultStats { get; set; }
public ICollection<Results> Results { get; set; }
}
}
EF auto-matches FK properties with navigation properties based on conventions. Namely, it expects FK properties to be named the same as navigation properties, just with Id at the end. In other words, for it to automatically match up OwnerId, you'd need a navigation property like:
public User Owner { get; set; }
Since your navigation property is UserId, it's actually looking for a property named UserIdId.
If you don't want to follow conventions, then you must either use the ForeignKey attribute or fluent config to tell EF which property belongs with which.
That said, there's some pretty major issues with your naming of things here. First, entities should always be singular User, not Users. Second, you should not have navigation properties that end with Id: e.g., User, not UserId. Only actual PK or FK properties should end with with Id. Finally, don't prefix properties on your entity with the entity name. This last one is mostly for readability. Which is more natural: competition.Id or competition.CompetitionId? Likewise with CompetitionName; it should just be Name. And, for what it's worth, you don't need Required for either a primary key or a non-nullable type (such as int). In either case, the property is required by default.
I have these entities:
public class Parent
{
public int Foo { get; set; }
public Child C { get; set; }
}
public class Child
{
public string Name { get; set; }
}
I have query which fetches all Parent entities from the database. Then I keep them in memory, and filter them using LINQ queries.
I have noticed that when I do the DB query, NH selects all the Parent entities in one query (and of course fills the Foo property), and for each Parent I access with LINQ, NH fetches the infos of each Child.
How can I do to fetch all infos I need in one unique DB, and use the data with LINQ without it to generate additional DB trips?
Should I use the AliasToBeanResultTransformer? If so, must I create a DTO which will store the infos, like:
public class ParentDTO
{
public int Foo { get; set; }
public string ChildName { get; set; }
}
or must I still use the Parent class?
Thanks in advance
You can eagerly load the children for this query like this (using QueryOver syntax)
public IList<Parent> FindAllParentsWithChildren()
{
ISession s = // Get session
return s.QueryOver<Parent>()
.Fetch(p => p.C).Eager
.List<Parent>();
}
An alternative is to change your HBM files to indicate that Child is eagerly loaded by default. Then you won't need to alter your query.
You need to tell NHibernate not to use lazy loading for the relationship between the Parent and Child entities.
I have an entity like this. Let's say that name is the only :
public class MyEntity
{
public Guid Id { get; set; } // immutable
public string Name { get; set; } // can be changed
// These are not exposed on the domain layer. They're just bookkeeping fields.
public string CreatedBy { get; set; }
public DateTime DateCreated { get; set; }
public string UpdatedBy { get; set; }
public DateTime DateUpdated { get; set; }
}
If I populate the 2 "updated" fields before calling SaveChanges(), my entity will be erroneously marked changed if Name hasn't changed. Therefore, I need an event on the DbContext to hook into in order to populate those 2 fields just before committing the unit of work, but only on entities that have actually changed.
Does such an event exist? Can nHibernate do this?
In ObjectContext API you have event SavingChanges directly on ObjectContext. You can handle this event and use ObjectStateManager to find all modified / added entities and deal with them as you need.
In both ObjectContext API and DbContext API you can override SaveChanges method directly and do the same.
Fluent NHibernate is just NHibernate's extension for fluent mapping in code. NHibernate has much better support for such scenarios - it offers custom listeners to deal with this.
i have one problem (obviously :) )
Is it possible to make dynamic queries in nHibernate in that way...
I have many tables (let we say: User, City, Country, Continet,...) is it possible to flaten this data so i do not need to know joins between this tables (get continent for user, without making join user.city, city.country, coutry.continent)?
The point is i want to some kind flatten data, so user can dynamically select data on user interface without knowing data model behind application?
It will be great that someone gave me at least idea how to make this, or if it's possible...
One example on web is GoogleAnalytics Custom reports (you can drag dimensions and metrics on UI and get results)
You said you're using Fluent NHibernate, which means that, assuming your domain model is structured correctly, you should not need to use any joins.
"Flattening" the data is a UI concern, not a database concern, so you shouldn't flatten your data model or simplify it for the UI unless you absolutely have to.
Let's assume you have the following entities:
public class User
{
public virtual string Name { get; set; }
public virtual City City { get; set; }
}
public class City
{
public virtual string Name { get; set; }
public virtual Country { get; set; }
}
public class Country
{
public virtual string Name { get; set; }
}
If you want to filter users by a certain country, the LINQ query for this (assuming NHibernate 3) would be:
var country = session.Single<Country>(x => x.Name == "Africa");
session.Query<User>().Where(x => x.City.Country == country);
I'm getting the "Save unsaved transient entities" error in NHibernate. I have an aggregate root, neighborhood that contains addresses and person, here's some quick pseudo code to explain the relationship:
public class Neighborhood {
public virtual int Id { get; set; }
public virtual IList<Address> Addresses { get; set; }
}
public class Address {
public virtual int Id { get; set; }
public virtual string Address { get; set; }
public virtual Person Person { get; set; } //Assume only one person per address
}
public class Person {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
In my Neighborhood map I have:
mapping.HasMany(x => x.Addresses)
.Inverse()
.KeyColumn("NeighborhoodFk")
.Cascade.All()
.AsBag();
In my code I will often want to create a and associate an Address and Person at the same time:
var address = new Address();
var person = new Person();
var address.Person = person;
var neighborhood = neighborhoodRepository.Get(id);
neighborhood.Add(address);
neighborhoodRepository.DbContext.BeginTransaction();
neighborhoodRepository.SaveOrUpdate(neighborhood);
neighborhoodRepository.DbContext.CommitTransation();
I will get the "unsaved transient entities" error on the Person entity because it is attached to the transient entity Address.
The only way I can see around this is to save the address first, make another call to the database to update neighborhood after the update, search for the address I just added, attach the person and then save again.
Is there something I'm missing to make this easier? This seems like a common use case and I don't want to be making a bunch of roundtrips to the database.
Make sure you're setting the "Cascade" attribute of your mapping from Address to Person to be "save-update" or "all". You have the cascade from Neighborhood to Address, but you didn't state that this lower cascade was present. If it isn't, you're getting this error not because a Person is attached to a transient Address, but because the Address references a transient Person.
If this cascade cannot be made for whatever reason, save the Person first, then save the Neighborhood, which will cascade to the Address, and the ORM will find the referenced Person in its session and set up the reference. This MAY result in some extra "round trips" depending on if you're letting NH or the DB generate autonumber columns. NHibernate is tricky in that it will make DB calls when it's good and ready, and that may be after the entire object graph is in the NH session, or just the person. Either way, it will make an Insert call into the DB for each object being persisted, so it will make multiple "roundtrips" no matter what the code to add the items to the session looks like.