Get existing entity if it exists or create a new one - nhibernate

I'm importing data that may or may not exist already in my database. I'd like NHibernate to associate any entities with the existing db one if it exists (probably just setting the primary key/id), or create a new one if it doesn't. I'm using S#arp architecture for my framework (MVC 2, NHibernate, Fluent).
I've added the [HasUniqueDomainSignature] attribute to the class, and a [DomainSignature] attribute to the properties I want to use for comparison. The only way I can think to do it (which is not an acceptable solution and may not even work) is the following (psuedo C#):
foreach (Book importedBook in importedBooks){
foreach (Author author in importedBook.Authors){
if (!author.IsValid()){ // NHibernate Validator will check DomainSignatures
author = _authorRepository.GetByExample(author); // This would be to get the db object with the same signature,
//but I don't think I could even update this as I iterate through it.
}
}
}
As you can see, this is both messy, and non-sensical. Add to that the fact that I've got a half dozen associations on the Book (subject, format, etc), and it doesn't make any sense. There's got to be an easy way to do this that I'm missing. I'm not a novice with NHibernate, but I'm definitely not an expert.

I might not be understanding the problem, but how can the data "may or may not exist in the database"? For example, if a Book has 2 Authors, how is the relationship stored at the database level if the Author doesn't exist?
It seems as if you're trying to use NHibernate to import your data (or create an entity if it doesn't exist) which doesn't seem correct.

Most database implementations support a conditional UPDATE-or-INSERT syntax. Oracle, for example, has a MERGE command. In combination with a Hibernate <sql-insert> block in your mapping you should be able to work something out. I don't know Fluent but I assume it supports this too.

Just realize I never gave an answer or approved another's answer. I ended up just writing a new SaveOrUpdate which takes a parameter to check for existing before persisting. I also added an attribute to my domain models to overwrite when saving/updating (although in retrospect it's only on updating that it'd be overwriting).
Here's the code if it can help anyone else in this dilemma:
public TEntity SaveOrUpdate<TEntity>(TEntity entity, bool checkForExistingEntity)
{
IRepository<TEntity> repository = new Repository<TEntity>();
if (checkForExistingEntity) {
if (entity is Entity) {
IEnumerable<PropertyInfo> props = (entity as Entity).GetSignatureProperties();
Dictionary<string, object> parameters =
props.ToDictionary(propertyInfo => propertyInfo.Name, propertyInfo => propertyInfo.GetValue(entity, null));
TEntity duplicateEntity = repository.FindOne(parameters);
if (duplicateEntity != null) {
// Update any properties with the OverwriteOnSaveUpdate attribute
foreach (var property in RepositoryHelper.GetUpdatableProperties(typeof(TEntity)))
{
object initialValue = property.GetValue(entity, null);
property.SetValue(duplicateEntity, initialValue, null);
}
// Fill in any blank properties on db version
foreach (var property in typeof(TEntity).GetProperties())
{
if (property.GetValue(duplicateEntity, null) == null) {
object initialValue = property.GetValue(entity, null);
property.SetValue(duplicateEntity, initialValue, null);
}
}
return duplicateEntity;
}
}
}
return SaveOrUpdate(entity);
}

Related

Why is my record being deleted from the db when I attempt to update the record from entity framework MVC?

When I attempt to update a record from entity framework the record is being deleted from the table. There are no errors thrown so it really has me baffled what is happening.
I am fairly new to entity framework and asp.net. I've been learning it for about a month now.
I can update the record without any issues from SQL Server but not from vs. Here is the code to update the db:
// GET: /Scorecard/Edit/5
public ActionResult Edit(int id, string EmployeeName)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CRS_Monthly crs_monthly = GetAgentById(id);
crs_monthly.EmployeeName = EmployeeName;
if (crs_monthly == null)
{
return HttpNotFound();
}
return View(crs_monthly);
}
// POST: /Scorecard/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="REC_ID,Cur_Plan,Plan_Update,Comments,Areas_Improve,Strengths,UPDATED_BY,UPDATED_TIME,Agent_Recognition")] CRS_Monthly crs_monthly)
{
if (ModelState.IsValid)
{
crs_monthly.UPDATED_TIME = DateTime.Now;
crs_monthly.UPDATED_BY = Request.LogonUserIdentity.Name.Split('\\')[1];
db.Entry(crs_monthly).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(crs_monthly);
}
When I run the debugger crs_monthly is valid and looks fine until db.SaveChanges(). Any help is greatly appreciated!
You should never save an instance of your entity created from a post, especially when you're utilizing Bind to restrict which properties are bound from the post data. Instead, always pull the entity fresh from the database and map the posted values on to it. This ensures that no data is lost.
Using Bind is a horrible practice, anyways. The chief problem with it is that all your properties are listed as string values, and you're introducing maintenance concerns. If remove one of these properties or change the name, the Bind list is not automatically updated. You must remember to change every single instance. Worse, if you add properties, you have to remember to go back and include them in this list or else your data just gets silently dropped with no notice.
If you need to only work with a subset of properties on your entity, create a view model containing just those properties. Then, again, map the posted values from your view model onto an instance of your entity pulled fresh from the database.

Change tracking aggregate root in DDD

This question is largely based on the article NHibernate – Automatic change tracking for aggregate roots in DDD scenarios
Although the logic in the article seems sound I have yet to find an implementation solution that will cover all use cases.
The problem seems to be related to the following paragraph from the article
There is a slight problem here that we may generate several updates per transaction here, but I am not worried about that overly much, it is fairly simple to resolve (by keeping track of the entity and not updating if we already updated in the current transaction), so I’ll leave it up to you.
Following the article we simply force a version update whenever we update a related entity within the aggregate root. However in cases where both aggregate root and related entity are 'dirty' this will cause a double update on the aggregate root. This causes nhibernate to fall over as the second version update triggered by default from the dirty aggregate root expects the version to be the same as what was loaded from the db.
I've attempted to put a check into the 'PreInsertEventListener' and 'PreUpdateEventListener' checking if the aggregate root is dirty when updating a related entity. If this is the case then ignore the forced update of version.
public bool OnPreUpdate(PreUpdateEvent updateEvent)
{
var rootFinder = updateEvent.Entity as ICanFindMyAggregateRoot;
if (rootFinder == null)
return false;
if (!updateEvent.Session.IsAggregateRootDirty(rootFinder.MyRoot))
{
updateEvent.Session.Lock(rootFinder.MyRoot, LockMode.Force);
}
return false;
}
public static class SessionExtensions
{
public static bool IsAggregateRootDirty(this ISession session, IAggregateRoot entity)
{
ISessionImplementor sessionImplementation = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImplementation.PersistenceContext;
IEntityPersister entityPersister = sessionImplementation.GetEntityPersister(null, entity);
EntityEntry entityEntry = persistenceContext.GetEntry(entity);
if ((entityEntry == null) && (entity is INHibernateProxy))
{
INHibernateProxy proxy = entity as INHibernateProxy;
object obj = sessionImplementation.PersistenceContext.Unproxy(proxy);
entityEntry = sessionImplementation.PersistenceContext.GetEntry(obj);
}
object[] oldState = entityEntry.LoadedState;
object[] currentState = entityPersister.GetPropertyValues(entity, sessionImplementation.EntityMode);
int[] findDirty = entityEntry.Persister.FindDirty(currentState, oldState, entity, sessionImplementation);
var hasDirtyCollection = currentState.OfType<IPersistentCollection>().Any(x => x.IsDirty);
return (findDirty != null) || hasDirtyCollection;
}
}
This solution does seem to work albeit I still need to test it with few more use cases. However I feel as if this solution is a bit heavy handed and was hoping for a solution more along what was outlined in the article.
Is there a way to detect weather the version has already been updated in the same transaction or will be, or a simple way to keep track of entities within the transaction set have its version updated.
Thanks.

Raven DB: How can I delete all documents of a given type

More specifically in Raven DB, I want to create a generic method with a signature like;
public void Clear<T>() {...
Then have Raven DB clear all documents of the given type.
I understand from other posts by Ayende to similar questions that you'd need an index in place to do this as a batch.
I think this would involve creating an index that maps each document type - this seems like a lot of work.
Does anyone know an efficient way of creating a method like the above that will do a set delete directly in the database?
I assume you want to do this from the .NET client. If so, use the standard DocumentsByEntityName index:
var indexQuery = new IndexQuery { Query = "Tag:" + collectionName };
session.Advanced.DocumentStore.DatabaseCommands.DeleteByIndex(
"Raven/DocumentsByEntityName",
indexQuery,
new BulkOperationOptions { AllowStale = true });
var hilo = session.Advanced.DocumentStore.DatabaseCommands.Get("Raven/H‌​ilo/", collectionName);
if (hilo != null) {
session.Advanced.DocumentStore.DatabaseCommands.Delete(hilo.‌​Key, hilo.Etag);
}
Where collectionName is the actual name of your collection.
The first operation deletes the items. The second deletes the HiLo file.
Also check out the official documentation - How to delete or update documents using index.
After much experimentation I found the answer to be quite simple, although far from obvious;
public void Clear<T>()
{
session.Advanced.DocumentStore.DatabaseCommands.PutIndex(indexName, new IndexDefinitionBuilder<T>
{
Map = documents => documents.Select(entity => new {})
});
session.Advanced.DatabaseCommands.DeleteByIndex(indexName, new IndexQuery());
}
Of course you almost certainly wouldn't define your index and do your delete in one go, I've put this as a single method for the sake of brevity.
My own implementation defines the indexes on application start as recommended by the documentation.
If you wanted to use this approach to actually index a property of T then you would need to constrain T. For example if I have an IEntity that all my document classes inherit from and this class specifies a property Id. Then a 'where T : IEntity' would allow you to use that property in the index.
It's been said in other places, but it's also worth noting that once you define a static index Raven will probably use it, this can cause your queries to seemingly not return data that you've inserted:
RavenDB Saving to disk query
I had this problem as well and this is the solution that worked for me. I'm only working in a test project, so this might be slow for a bigger db, but Ryan's answer didn't work for me.
public static void ClearDocuments<T>(this IDocumentSession session)
{
var objects = session.Query<T>().ToList();
while (objects.Any())
{
foreach (var obj in objects)
{
session.Delete(obj);
}
session.SaveChanges();
objects = session.Query<T>().ToList();
}
}
You can do that using:
http://blog.orangelightning.co.uk/?p=105

How to work around NHibernate caching?

I'm new to NHibernate and was assigned to a task where I have to change a value of an entity property and then compare if this new value (cached) is different from the actual value stored on the DB. However, every attempt to retrieve this value from the DB resulted in the cached value. As I said, I'm new to NHibernate, maybe this is something easy to do and obviously could be done with plain ADO.NET, but the client demands that we use NHibernate for every access to the DB. In order to make things clearer, those were my "successful" attempts (ie, no errors):
1
DetachedCriteria criteria = DetachedCriteria.For<User>()
.SetProjection(Projections.Distinct(Projections.Property(UserField.JobLoad)))
.Add(Expression.Eq(UserField.Id, userid));
return GetByDetachedCriteria(criteria)[0].Id; //this is the value I want
2
var JobLoadId = DetachedCriteria.For<User>()
.SetProjection(Projections.Distinct(Projections.Property(UserField.JobLoad)))
.Add(Expression.Eq(UserField.Id, userid));
ICriteria criteria = JobLoadId.GetExecutableCriteria(NHibernateSession);
var ids = criteria.List();
return ((JobLoad)ids[0]).Id;
Hope I made myself clear, sometimes is hard to explain a problem when even you don't quite understand the underlying framework.
Edit: Of course, this is a method body.
Edit 2: I found out that it doesn't work properly for the method call is inside a transaction context. If I remove the transaction, it works fine, but I need it to be in this context.
I do that opening a new stateless session for geting the actual object in the database:
User databaseuser;
using (IStatelessSession session = SessionFactory.OpenStatelessSession())
{
databaseuser = db.get<User>("id");
}
//do your checks
Within a session, NHibernate will return the same object from its Level-1 Cache (aka Identity Map). If you need to see the current value in the database, you can open a new session and load the object in that session.
I would do it like this:
public class MyObject : Entity
{
private readonly string myField;
public string MyProperty
{
get { return myField; }
set
{
if (value != myField)
{
myField = value;
DoWhateverYouNeedToDoWhenItIsChanged();
}
}
}
}
googles nhforge
http://nhibernate.info/doc/howto/various/finding-dirty-properties-in-nhibernate.html
This may be able to help you.

With NHibernate, how can I create an INHibernateProxy?

After lots of reading about serialization, I've decided to try to create DTOs. After more reading, I decided to use AutoMapper.
What I would like to do is transform the parent (easy enough) and transform the entity properties if they've been initialized, which I've done with ValueResolvers like below (I may try to make it generic once I get it fully working). This part works.
public class OrderItemResolver : ValueResolver<Order, OrderItem>
{
protected override OrderItem ResolveCore(Order source)
{
// could also use NHibernateUtil.IsInitialized(source.OrderItem)
if (source.OrderItem is NHibernate.Proxy.INHibernateProxy)
return null;
else
return source.OrderItem;
}
}
}
When I transform the DTO back to an entity, for the entities that weren't initialized, I want to create a proxy so that if the entity wants to access it, it can. However, I can't figure out how to create a proxy. I'm using Castle if that's relevant.
I've tried a bunch of things with no luck. The below code is a mess, mainly because I've been trying things at random without knowing what I should be doing. Anybody have any suggestions?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
{
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>(); // Castle.Core.Interceptor.
//OrderItem OrderItem = new ProxyGenerator().CreateClassProxy<OrderItem>();
//OrderItem.Id = source.OrderItemId;
//OrderItem OrderItem = new OrderItem();
//var proxy = new OrderItem() as INHibernateProxy;
//var proxy = OrderItem as INHibernateProxy;
//return (OrderItem)proxy.HibernateLazyInitializer
//ILazyInitializer proxy = new LazyInitializer("OrderItem", OrderItem, source.OrderItemId, null, null, null, null);
//return (OrderItem)proxy;
//return (OrderItem)proxy.HibernateLazyInitializer.GetImplementation();
//return OrderItem;
IProxyTargetAccessor proxy = new Castle.Core.Interceptor.
var initializer = new LazyInitializer("OrderItem", typeof(OrderItem), source.OrderItemId, null, null, null, null);
//var proxyFactory = new SerializableProxyFactory{Interfaces = Interfaces, TargetSource = initializer, ProxyTargetType = IsClassProxy};
//proxyFactory.AddAdvice(initializer);
//object proxyInstance = proxyFactory.GetProxy();
//return (INHibernateProxy) proxyInstance;
return null;
//OrderItem.Id = source.OrderItemId;
//return OrderItem;
}
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
Thanks,
Eric
Maybe I over complicated it. This seems to work. Anybody see any issues with it?
public class OrderItemDTOResolver : ValueResolver<OrderDTO, OrderItem>
{
protected override OrderItem ResolveCore(OrderDTO source)
{
if (source.OrderItem == null)
return NHibernateSessionManager.Instance.Session.GetISession().Load<OrderItem>(source.AgencyId);
else
return OrderItemDTO.Unmap(source.OrderItem);
}
}
This may be one of those cases where the answer is "don't", or at least "you probably shouldn't". If you're mapping DTOs into NHibernate mapped objects directly you're not really using the mapped objects as domain objects, just as a fancy way to push data in and out of the database. This of course may be all you're after but having done this myself in the past I've found that it's problematic trying to use the same DTO data format in both directions. If you're going cross-process you've turned the service into a (difficult to maintain) CRUD layer. If you're in the same process you're doing unnecessary data shuffling with DTOs.
Sending DTOs out is fine, but consider projecting the data into a format more closely aligned with what the client actually needs. What you get back is better expressed in specific DTOs that express only the data needed to perform the actual action (Command objects, essentially). With a few automatic properties they're trivial to construct. You can then have a business method that performs the necessary action with only the necessary information, and that in a format suited to the action being performed. My primary use of AutoMapper (which does rock) these days is to translate incoming DTOs into types that domain methods can consume.
Also, public setters on mapped objects are undesirable because they allow the object to be manipulated by any code without any validation. This means any modification to them can leave them in an invalid state.
If you don't really care about the above (and it's not always applicable) the way you load individual instances does leave you open do doing many individual database loads which is a potential performance issue.