Given the following
[Test]
public void VerifyMappings()
{
new PersistenceSpecification<Address>(Session)
.CheckProperty(x => x.AddressLine1, "190 House 12")
.VerifyTheMappings();
}
The following will attempt to do a read and write to the datbase, however it leaves the record. Is it possible to delete this record using the fluent framework?
Just use something like this in your [TearDown]:
var currentSession = NHibernateSession.Current;
if (currentSession.Transaction.IsActive) {
currentSession.Flush();
currentSession.Transaction.Rollback();
}
That will rollback the current transaction.
Related
I have two different processes (on different machines) that are reading and updating a database record.
The rule I need to ensure is that the record must only be updated if the value of it, lets say is "Initial". Also, after the commit I would want to know if it actually got updated from the current process or not (in case if value was other than initial)
Now, the below code performs something like:
var record = context.Records
.Where(r => (r.id == id && r.State == "Initial"))
.FirstOrDefault();
if(record != null) {
record.State = "Second";
context.SaveChanges();
}
Now couple of questions
1) From looking at the code it appears that after the record is fetched with state "Initial", some other process could have updated it to state "Second" before this process performs SaveChanges.
In this case we are unnecessarily overwriting the state to the same value. Is this the case happening here ?
2) If case 1 is not what happens then EntityFramework may be translating the above to something like
update Record set State = "Second" where Id = someid and State = "Initial"
and performing this as a transaction. This way only one process writes the value. Is this the case with EF default TransactionScope ?
In both cases again how do I know for sure that the update was made from my process as opposed to some other process ?
If this were in-memory objects then in code it would translate to something like assuming multiple threads accessing same data structure
Record rec = FindRecordById(id);
lock (someobject)
{
if(rec.State == "Initial")
{
rec.State = "Second";
//Now, that I know I updated it I can do some processing
}
}
Thanks
In general there are 2 main concurrency patterns that can be used:
Pessimistic concurrency: You lock a row to prevent others from unexpectedly changing the data you are currently attempting to update. EF does not provide any native support for this type of concurrency pattern.
Optimistic concurrency: Citing from EF's documentation: "Optimistic concurrency involves optimistically attempting to save your entity to the database in the hope that the data there has not changed since the entity was loaded. If it turns out that the data has changed then an exception is thrown and you must resolve the conflict before attempting to save again." This pattern is supported by EF, and can be used rather simply.
Focusing on the optimistic concurrency option, which EF does support, let's compare how your example behaves with and without EF's optimistic concurrency control handling. I'll assume you are using SQL Server.
No concurrency control
Let's start with the following script in the database:
create table Record (
Id int identity not null primary key,
State varchar(50) not null
)
insert into Record (State) values ('Initial')
And here is the code with the DbContext and Record entity:
public class MyDbContext : DbContext
{
static MyDbContext()
{
Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext() : base(#"Server=localhost;Database=eftest;Trusted_Connection=True;") { }
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new Record.Configuration());
}
}
public class Record
{
public int Id { get; set; }
public string State { get; set; }
public class Configuration : EntityTypeConfiguration<Record>
{
public Configuration()
{
this.HasKey(t => t.Id);
this.Property(t => t.State)
.HasMaxLength(50)
.IsRequired();
}
}
}
Now, let's test your concurrent update scenario with the following code:
static void Main(string[] args)
{
using (var context = new MyDbContext())
{
var record = context.Records
.Where(r => r.Id == 1 && r.State == "Initial")
.Single();
// Insert sneaky update from a different context.
using (var sneakyContext = new MyDbContext())
{
var sneakyRecord = sneakyContext.Records
.Where(r => r.Id == 1 && r.State == "Initial")
.Single();
sneakyRecord.State = "Sneaky Update";
sneakyContext.SaveChanges();
}
// attempt to update row that has just been updated and committed by the sneaky context.
record.State = "Second";
context.SaveChanges();
}
}
If you trace the SQL, you will see that the update statement looks like this:
UPDATE [dbo].[Record]
SET [State] = 'Second'
WHERE ([Id] = 1)
So, in effect, it doesn't care that another transaction sneaked in an update. It just blindly writes over whatever the other update did. And so, the final value of State in the database for that row is 'Second'.
Optimistic concurrency control
Let's adjust our initial SQL script to include a concurrency control column to our table:
create table Record (
Id int identity not null primary key,
State varchar(50) not null,
Concurrency timestamp not null -- add this row versioning column
)
insert into Record (State) values ('Initial')
Let's also adjust our Record entity class (the DbContext class stays the same):
public class Record
{
public int Id { get; set; }
public string State { get; set; }
// Add this property.
public byte[] Concurrency { get; set; }
public class Configuration : EntityTypeConfiguration<Record>
{
public Configuration()
{
this.HasKey(t => t.Id);
this.Property(t => t.State)
.HasMaxLength(50)
.IsRequired();
// Add this config to tell EF that this
// property/column should be used for
// concurrency checking.
this.Property(t => t.Concurrency)
.IsRowVersion();
}
}
}
Now, if we try to re-run the same Main() method we used for the previous scenario, you will notice a change in how the update statement is generated and executed:
UPDATE [dbo].[Record]
SET [State] = 'Second'
WHERE (([Id] = 1) AND ([Concurrency] = <byte[]>))
SELECT [Concurrency]
FROM [dbo].[Record]
WHERE ##ROWCOUNT > 0 AND [Id] = 1
In particular, notice how EF automatically includes the column defined for concurrency control in the where clause of the update statement.
In this case, because there was in fact a concurrent update, EF detects it, and throws a DbUpdateConcurrencyException exception on this line:
context.SaveChanges();
And so, in this case, if you check the database, you'll see that the State value for the row in question will be 'Sneaky Update', because our 2nd update failed to pass the concurrency check.
Final thoughts
As you can see, there isn't much that needs to be done to activate automatic optimistic concurrency control in EF.
Where it gets tricky though is, how do you handle the DbUpdateConcurrencyException exception when it gets thrown? It will largely be up to you to decide what you want to do in this case. But for further guidance on the topic, you'll find more information here: EF - Optimistic Concurrency Patterns.
We have a userobject split between two tables, a person table and user table.
public UserMap()
{
Table("Person");
Id(x => x.PersonId, "PersonId");
Map(x => x.Name, "Name");
Join("User", s => {
s.Fetch.Join();
s.KeyColumn("PersonId");
s.Map(x => x.Username, "Username");
s.Map(x => x.Password, "Password");
});
}
When doing a insert, nHibernate will first insert Name into the Person table, and then Username and Password into the User table.
The problem arise when the insertion into the User-table fails (like trying to insert a user with a username that is already taken). The transaction fails, but the insertion into the Person table is not rolled back.
public User Save(User user)
{
var session = SessionHelper.GetCurrent();
using (var dbTransaction = session.BeginTransaction())
{
try
{
session.SaveOrUpdate(user);
dbTransaction.Commit();
}
catch (Exception)
{
dbTransaction.Rollback();
throw;
}
}
return user;
}
Our SessionFactory is also set up with Fluent NHibernate
public ISessionFactory BuildSessionFactory(string connectionString)
{
return Fluently.Configure().Database(OracleClientConfiguration.Oracle10.ConnectionString(c => c.Is(connectionString))
.Provider<OracleDriverConnectionProvider>()
.Driver<OracleDataClientDriver>()
.ShowSql)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
.BuildSessionFactory();
}
I have put the logfile from a failing session in a gist. The User entity is a little more complex in real life, and there are some authentication stuff going on, so the log file want match 1:1 with the painted picture...
Seems like a similar problem was solved here. NHibernate will not revert you changes in the session when you rollback your transaction, i.e if you flush your session after the rollback, you will experience the problems you describe. To ensure no changes in your entities, when you rollback, you have to also close the session.
Add User object in Person Object and Save Person Object only. Use Cascade.All() in Mapping file. or Second option is use TransactionScope.
Instead of closing the session, you can use session.Clear() after you have roleld back the transaction, which will remove all pending writes and make session as good as new.
That did the trick for my application.
I would like to approach database versioning and automated upgrades in NHibernate from a different direction than most of the strategies proposed out there.
As each object is defined by an XML mapping, I would like to take size and checksum for each mapping file/ configuration and store that in a document database (raven or something) along with a potential custom update script. If no script is found, use the NHibernate DDL generator to update the object schema. This way I can detect changes, and if I need to make DML changes in addition to DDL, or perform a carefully ordered transformation, I can theoretically do so in a controlled, testable manner. This should also maintain a certain level of persistence-layer agnosticism, although I'd imagine the scripts would still necessarily be database system-specific.
The trick would be, generating the "old" mapping files from the database and comparing them to the current mapping files. I don't know if this is possible. I also don't know if I'm missing anything else that would make this strategy prohibitively impractical.
My question, then: how practical is this strategy, and why?
what i did to solve just that problem
version the database in a table called SchemaVersion
query the table to see if schema is up to date (required version stored in DAL), if yes goto 6.
get updatescript with version == versionFromBb from resources/webservices/...
run the script which also alters the schemaversion to the new version
goto 2.
run app
to generate the scripts i have used 2 options
support one rdbms: run SchemaUpdate to export into file and add DML statements manually
support multiple rdbms: use Nhibernate class Table to generate at runtime ddl to add/alter/delete tables and code which uses a session DML
Update:
"what method did you use to store the current version"
small example
something like this
public static class Constants
{
public static readonly Version DatabaseSchemaVersion = new Version(1, 2, 3, 4);
}
public class DBMigration
{
private IDictionary<Version, Action> _updates = new Dictionary<Version, Action>();
private Configuration _config;
private Dialect _dialect;
private IList<Action<ISession>> _actions = new List<Action<ISession>>(16);
private string _defaultCatalog;
private string _defaultSchema;
private void CreateTable(string name, Action<Table> configuretable)
{
var table = new Table(name);
configuretable(table);
string createTable = table.SqlCreateString(_dialect, _config.BuildMapping(), _defaultCatalog, _defaultSchema);
_actions.Add(session => session.CreateSQLQuery(createTable).ExecuteUpdate());
}
private void UpdateVersionTo(Version version)
{
_actions.Add(session => { session.Get<SchemaVersion>(1).Value = version; session.Flush(); });
}
private void WithSession(Action<session> action)
{
_actions.Add(action);
}
public void Execute(Configuration config)
{
_actions.Clear();
_defaultCatalog = config.Properties[NH.Environment.DefaultCatalog];
_defaultSchema = config.Properties[NH.Environment.DefaultSchema];
_config = config;
_dialect = Dialect.GetDialect(config.Properties);
using (var sf = _config.BuildSessionFactory())
using (var session = sf.OpenSession())
using (var tx = session.BeginTransaction())
{
Version dbVersion = session.Get<SchemaVersion>(1).Value;
while (dbVersion < Constants.DatabaseSchemaVersion)
{
_actions.Clear();
_updates[dbVersion].Invoke(); // init migration, TODO: error handling
foreach (var action in _actions)
{
action.Invoke(session);
}
tx.Commit();
session.Clear();
dbVersion = session.Get<SchemaVersion>(1).Value;
}
}
}
public DBMigration()
{
_updates.Add(new Version(1, 0, 0, 0), UpdateFromVersion1);
_updates.Add(new Version(1, 0, 1, 0), UpdateFromVersion1);
...
}
private void UpdateFromVersion1()
{
AddTable("Users", table => table.AddColumn(...));
WithSession(session => session.CreateSqlQuery("INSERT INTO ..."));
UpdateVersionTo(new Version(1,0,1,0));
}
...
}
I am trying to write a test for my NHibernate mappings that will automatically pick up and test any new mappings that get added.
At the moment I have a test that opens a session to a known test database then attempts to load the first entity of each type and asserts that it is not null.
This all works fine but it means that every time I add a new entity mapping, I need to remember to update the test.
So, what I want to do is to inspect the mappings and try to load one of each of the mapped entities, but the NHibernate Configuration object that the sessionfactory is built from is not visible to my test so I was wondering if there is a way to access a list of mapped entities from the session or do I need to expose the original Configuration instead?
You can get SessionFactory from Session and SessionFactory has method GetAllClassMetadata() which returns list of IClassMetadata. And from IClassMetadata you can get MappedClass (GetMappedClass())
But you will need some extra work to get subclasses. This code can help:
var metaData = this.session.SessionFactory.GetClassMetadata(baseClass);
if (metaData != null && metaData.HasSubclasses)
{
foreach (string entityName in ((NHibernate.Persister.Entity.IEntityPersister)metaData).EntityMetamodel.SubclassEntityNames)
{
var metadata = this.session.SessionFactory.GetClassMetadata(entityName);
result.Add(metadata.GetMappedClass(EntityMode.Poco));
}
}
I expose the configuration object and do a mapping that queries all of my entities like this. It will output all errors from each of my mappings.:
[TestMethod()]
public void AllNHibernateMappingsAreOkay()
{
bool failed = false;
log4net.Config.XmlConfigurator.Configure();
using (ISession session = SessionFactory.GetCurrentSession())
{
foreach (var s in SessionFactory.GetConfig().ClassMappings)
{
try
{
SessionFactory.GetCurrentSession().CreateQuery(string.Format("from {0} e", s.MappedClass.Name))
.SetFirstResult(0).SetMaxResults(50).List();
}
catch (Exception ex)
{
failed = true;
log.ErrorFormat("\r\n\r\n {0} \r\n {1} \r\n\r\n", ex.Message, ex.InnerException.Message);
}
}
}
Assert.IsFalse(failed, "One or more mappings have errors in them. Please refer to output or logs.");
}
if you have only one row per entity then you could issue session.QueryOver<object>().List();
This question already has answers here:
NHibernate update on single property updates all properties in sql
(2 answers)
Closed 5 years ago.
I am using Fluent NHibernate to do my NHibernate mappings, but now I have come to a problem that I am not sure how to solve. A simplified version of the problem follows.
I have a user class:
public class User {
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
This is the associated Fluent NHibernate Class Map
public class UserMap : ClassMap<User> {
public UserMap() {
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
}
}
I have two web forms. One form allows me to change the users first name, and the second form allows me to change the users last name. What I am trying to achieve is a simple SQL statement like this:
For the first form:
UPDATE [users] SET firstname='new first name' WHERE id=1
For the second form:
UPDATE [users] SET lastname='new last name' WHERE id=1
Currently NHibernate performs the following SQL on my database:
UPDATE [users] SET firstname=null, lastname='new last name' WHERE id=1
The problem in the real world application, is that there are too many properties to update on some big objects (as well as access restrictions), and it seems pointless to update the whole object, when all I want / am allowed to do is update a single property.
I am hoping that someone can provide some advice as to how I can realise this, or point me in the right direction to solve this.
Hibernate's doing the right thing, but your problem indicates that your schema needs some normalization.
Ok, that works, Thanks for the help and tips Queen3!
here is how I sovled it:
using (var sf = Repository.CreateSessionFactory()) {
using (var s = sf.OpenSession()) {
using (var t = session.BeginTransaction()) {
var existingUser = s.Get<User>(editedUser.Id);
existingUser.LastName = editedUser.LastName;
s.SaveOrUpdate(existingUser);
t.Commit();
}
}
}
Although this does work, it requires that I retrieve the User from the database first and work within the same session. The good thing is that the sql statement that is generated just updates the dirty LastName field. :-)
I am unable to get it to work with a detached instance of the user, this is similar to how I was doing it before, which resulted in every field of the user being updated.
using (var sf = Repository.CreateSessionFactory()) {
using (var s = sf.OpenSession()) {
using (var t = session.BeginTransaction()) {
s.SaveOrUpdate(editedUser);
t.Commit();
}
}
}