NHibernate: Access another column's value in a UserType - nhibernate

I'm trying to make a UserType that hashes a value. The issue I'm having is getting access to the Salt that sits in the same table.
void IUserType.NullSafeSet(IDbCommand cmd, object value, int index)
{
object paramVal = DBNull.Value;
if (!String.IsNullOrEmpty((string)value))
{
paramVal = ComputeHash((string)value, saltBytes?);
}
IDataParameter parameter = (IDataParameter)cmd.Parameters[index];
parameter.Value = paramVal;
}
I am uncertain on how to reliably access a database column of the same table to get the salt that was set.
I could do something like this to access the salt column:
byte[] saltValueBeingInsertedIntoDB = (IDataParameter)cmd.Parameters[1].Value;
It just seems so fragile to access it via index, as the order could change. I'd love it if I could access it off of the column name, but the column name (SoureColumn) is never populated.
How can I reliably access the Salt that exists in cmd.Parameters? Or is there a better way? (I have full control to change whatever is needed, except the NHibernate version).
Note: If I'm setting the salt somewhere else, it may make sense for me to also hash the value in that place, rather than using a UserType.
NHiberate 2.1.2.4000
Fluent NHibernate 1.1.0.685

To solve my issue, I chose not to use the UserType.
Instead, I create a static instance of my Ciphering service on the object(s) that need it, and then use a helper property to get/set the encrypted value. This works great for me.
public class Consumer
{
static Consumer()
{
CipherConsumerSsnService = new CipherConsumerSsnService();
}
public static ICipherConsumerSsnService CipherConsumerSsnService { get; set; }
public virtual long ID { get; private set; }
public virtual byte[] SSN { get; protected set; }
public virtual string GetDecryptedSsnOrSetSsnValueAndEncryptIt
{
get
{
return SSN != null ? CipherConsumerSsnService.Decrypt(SSN) : null;
}
set
{
SSN = value != null ? CipherConsumerSsnService.Encrypt(value) : null;
}
}
}
Note that this example doesn't use a salt, but you should!

Related

Can I programmatically determine the database "context" to use based on user credentials?

This is a followup to the question here, where the answer seems to refer to an overly-complicated and overly-specific (EF, which I'm not using - not even using an ORM).
There has to be a more straightforward way around this common scenario than the smoke, mirrors, and sorcery hinted at in that answer.
Note: I encased "context" in parenthesis because I'm not using EF, so it is not a literal "dbcontext" that I'm talking about here.
So I got to wondering: Could I set a global variable for each session when the user is authenticated and authorized?
e.g., when the user is authenticated/authorized, I would know which database context/contents should be served up to him.
So it seems I could set a value in Global.asax.cs' Application_Start() method and then either alter the RepositoriesInstaller (implementing IWindsorInstaller) class to conditionally register different concrete Repositories based on the user and what data he should have OR place conditional code in the concrete Repository itself to use this or that database instance.
Is this feasible? Is one way (altering the RepositoriesInstaller / altering the concrete Repositories class) preferred?
Have a look at this answer which will show you how to resolve the correct repository based on a key or value.
If you want to store it with the authorized user, you need to simple serialize the data and store it on the authenticated cookie:
Create a Model to represent the logged in information:
public class AuthenticatedMember
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int SiteNumber { get; set; }
}
Do something like a login in your controller:
var authenticatedMember = MembershipManager.ValidateLogin(model.Email, model.Password);
var cookie = FormsAuthentication.GetAuthCookie(authenticatedMember.Id.ToString(), false);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, authenticatedMember.ToJson(), ticket.CookiePath);
cookie.Value = FormsAuthentication.Encrypt(newTicket);
Response.Cookies.Add(cookie);
Then use a model binder to deserialize the AuthenticatedMember when you require:
public class AuthenticatedMemberModelBinder : IModelBinder
{
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
throw new InvalidOperationException("Cannot update instances");
}
if (controllerContext.RequestContext.HttpContext.Request.IsAuthenticated)
{
var cookie = controllerContext
.RequestContext
.HttpContext
.Request
.Cookies[FormsAuthentication.FormsCookieName];
if (null == cookie)
return null;
var decrypted = FormsAuthentication.Decrypt(cookie.Value);
if (!string.IsNullOrEmpty(decrypted.UserData))
{
return JsonConvert.DeserializeObject<AuthenticatedMember>(decrypted.UserData);
}
}
return null;
}
#endregion
}

NHibernate second level cache not working when using composite primary key?

What's the proper way to use a composite primary key in NHibernate so it will be amenable for caching?
I isolate the composite primary key similar to the last part of this post: http://devlicio.us/blogs/anne_epstein/archive/2009/11/20/nhibernate-and-composite-keys.aspx
But the second level cache isn't caching it.
If for surrogate key, it is caching, e.g.
var q = from p in session.Query<Product>()
select p;
q.Cacheable().ToList(); // hit database
// this doesn't hit the database, Get get its value from cache(via last query)
var px = secondSession.Get<Product>(1);
But when using composite primary key, the Get doesn't get its value from the cache:
var q = from pl in session.Query<ProductLanguage>()
select pl;
q.Cacheable().ToList(); // hit the database
// this hits the database, Get didn't get its value from cache(via last query)
var plx = secondSession.Get<ProductLanguage>(
new ProductLanguageCompositeKey { ProductId = 1, LanguageCode = "en" });
Is composite key(ProductLanguageCompositeKey here), even isolated it its own class(with Serializable attribute, Equals and GetHashCode) doesn't get cached?
How can we make an entity accessed via composite key cacheable?
For those suspecting if NHibernate's second level cache is not working when using composite primary key (caching works), check if your composite primary key's values are in pristine form. Solution to my caching problem:
SQL Server conversion fidelity from nvarchar to varbinary, then from varbinary to nvarchar
For caching a unique cache key is generated for ProductLanguage. This cache key is built from the composite key, which depends on the Product entity's hash code. If you use cross-session queries, NHibernate might return proxied or unproxied versions of Product, which will cause different hash codes and cause the cache lookup to miss the cached ProductLanguage entity.
The solution is to override the Equals and GetHashCode methods in order to return consistent values. The simplest way is to inherit the popular EntityBase class for all entities that have a surrogate Id key.
public abstract class EntityBase<T>
where T : EntityBase<T>
{
public virtual int Id { get; protected set; }
protected bool IsTransient { get { return Id == 0; } }
public override bool Equals(object obj)
{
return EntityEquals(obj as EntityBase<T>);
}
protected bool EntityEquals(EntityBase<T> other)
{
if (other == null)
{
return false;
}
// One entity is transient and the other is not.
else if (IsTransient ^ other.IsTransient)
{
return false;
}
// Both entities are not saved.
else if (IsTransient && other.IsTransient)
{
return ReferenceEquals(this, other);
}
else
{
// Compare transient instances.
return Id == other.Id;
}
}
// The hash code is cached because a requirement of a hash code is that
// it does not change once calculated. For example, if this entity was
// added to a hashed collection when transient and then saved, we need
// the same hash code or else it could get lost because it would no
// longer live in the same bin.
private int? cachedHashCode;
public override int GetHashCode()
{
if (cachedHashCode.HasValue) return cachedHashCode.Value;
cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
return cachedHashCode.Value;
}
// Maintain equality operator semantics for entities.
public static bool operator ==(EntityBase<T> x, EntityBase<T> y)
{
// By default, == and Equals compares references. In order to
// maintain these semantics with entities, we need to compare by
// identity value. The Equals(x, y) override is used to guard
// against null values; it then calls EntityEquals().
return Object.Equals(x, y);
}
// Maintain inequality operator semantics for entities.
public static bool operator !=(EntityBase<T> x, EntityBase<T> y)
{
return !(x == y);
}
}
and implemented:
public class Blog : EntityBase<Blog>
{
public virtual string Name { get; set; }
// This would be configured to lazy-load.
public virtual IList<Post> Posts { get; protected set; }
public Blog()
{
Posts = new List<Post>();
}
public virtual Post AddPost(string title, string body)
{
var post = new Post() { Title = title, Body = body, Blog = this };
Posts.Add(post);
return post;
}
}
public class Post : EntityBase<Post>
{
public virtual string Title { get; set; }
public virtual string Body { get; set; }
public virtual Blog Blog { get; set; }
public virtual bool Remove()
{
return Blog.Posts.Remove(this);
}
}
void Main(string[] args)
{
var post = session.Load<Post>(postId);
// If we didn't override Equals, the comparisons for
// "Blog.Posts.Remove(this)" would all fail because of reference equality.
// We'd end up be comparing "this" typeof(Post) with a collection of
// typeof(PostProxy)!
post.Remove();
// If we *didn't* override Equals and *just* did
// "post.Blog.Posts.Remove(post)", it'd work because we'd be comparing
// typeof(PostProxy) with a collection of typeof(PostProxy) (reference
// equality would pass!).
}
More info at https://stackoverflow.com/a/20110265/179494

NHibernate, a different object with the same identifier value was already associated with the session

I have been working with NHibernate, using Fluent NHibernate for mapping. I solved a lot of issues, and started to think myself as experienced in nhibernate.
However, this error is quite strange.
This is my model:
public class MessageNew
{
public virtual int Id { get; set; }
public virtual string Content { get; set; }
public virtual string Subject { get; set; }
public virtual User User { get; set; }
public virtual bool IsSent { get; set; }
public virtual string AmazonMessageId { get; set; }
}
And my mapping
public class MessageNewMap : ClassMap<MessageNew>
{
public MessageNewMap()
{
Id(x => x.Id);
Map(x => x.Content).CustomSqlType("text");
Map(x => x.Subject);
Map(x => x.AmazonMessageId);
Map(x => x.IsSent);
References(x => x.User);
}
}
Here where exception occurs:
foreach (var userToSend in usersToSend)
{
string body = MailHelper.BuildSomeBody()
if (userToSend != CurrentUser)
{
MessageNew message = new MessageNew
{
User = userToSend,
IsSent = false,
Content = body,
Subject = subject
};
session.Save(message); // Exception thrown
}
}
The exception details:
NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 1779, of entity: Models.MessageNew
at NHibernate.Engine.StatefulPersistenceContext.CheckUniqueness(EntityKey key, Object obj)
at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.Save(Object obj)
Id generator is database driven auto-increment id generator. (not hilo or any other). NHibernate version is 3.2.0 .
I have tried overloading Equals and GetHashCode, no luck.
The UnitOfWork pattern I am using requires not to commit transaction or flush session inside foreach loop. NHibernate says there is another object with same id, but all i am doing is inserting a new object, which does not have any identifier at all.
I am using the same structure all over my project, and it works well everywhere but this. I am suspicious that it might be because of "Content" property, which is text and set to a large string.
What am i missing here? Or NHibernate is missing something?
Sometimes it happend when we assign the object to the same new object. So first check your model and viewmodel that they are not same.
I had similar problem. I went through a lot of discussions, tutorials and forums, but after writing some unit tests, I realized:
1) session.Contains method works with instances
2)session.Save/SaveorUpdate works with ID
This error shows you have another instances of object with same ID in session.So, contains return false because you are working on different instances and Save/SaveorUpdate throws an exception because there is another object with same ID in session.
I've solved my problem like this(my problem was in Job Entity):
Job lJob = lSession.Load<Job>(this.ID);
if(lJob.ID==this.ID)
lSession.Evict(lJob);
lSession.SaveOrUpdate(this);
I hope it helps you
You can use Evict() to evict an object from a session and then you can do whatever you want.
This error occurs when you have the same object in another session.
messagenew should implement Equals and GetHashCode
public class MessageNew
{
public virtual int Id { get; set; }
public override bool Equals(object obj)
{
var other = obj as MessageNew;
return (other != null) && (IsTransient ? ReferenceEquals(this, other) : Id == other.Id;
}
private int? _cachedHashcode; // because Hashcode should not change
public override int GetHashCode()
{
if (_cachedHashcode == null)
_cachedHashcode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
return _cachedHashcode.Value;
}
public bool IsTransient { get { return Id == 0; } }
}
I read some NH code. It basically inserts the new instance into the database to get its id. Then it checks if the id generated by the database is actually unique. If not, you get this exception.
Your database is not generating unique ids. You most probably forgot to set it to an IDENTITY column.
OR the identity starts counting on 0 instead of 1.
That exception usually indicates that you have 2 separate instances of an object with the same identifier value which you are trying to manage over the same session.
You already have another instance of the entity with that id.
Two possible issues:
1 - Your comparison of the entity does not work. You could override equals as suggested or you could change your test case that you use prior to the save:
if (userToSend.Id != CurrentUser.Id)
2 - You are not generating a unique Id for your entity, you need to either assign an Id yourself, have NHibernate generate one or have your sql server do it for you. In your mapping it is implied that an Identity should be used (Fluents default) but have you set up the column in your database to be and Identity column?
My take: you are not declaring an Id generator. Therefore, as soon as you get two MessageNew instances in the session, they'll both have 0 as the Id.
maybe a bit late but hope this helps.
I had a similar problem when i was trying to save multiple instances of an object over the same session with an auto generated column on them. My solution was giving a diferent value and assign it mannually for each entity, so nhibernates doesn't recognize it as the same primary key for that entity.
[..]
};
session.Clear();
session.Save(message);
Try this, helped me.
Add below two lines before Session.Save or Session.SaveOrUpdate
Session.Clear();
Session.Flush();
This will clear all cached entities with the Session.

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.

NHibernate add unmapped column in interceptor

I'm trying to save a mapped entity using NHibernate but my insert to the database fails because the underlying table has a column that does not allow nulls and IS NOT mapped in my domain object. The reason it isn't mapped is because the column in question supports a legacy application and has no relevance to my application - so I'd like to not pollute my entity with the legacy property.
I know I could use a private field inside my class - but this still feels nasty to me. I've read that I can use an NHibernate interceptor and override the OnSave() method to add in the new column right before my entity is saved. This is proving difficult since I can't work out how to add an instance of Nhibernate.type.IType to the types parameter of my interceptor's OnSave.
My Entity roughly looks like this:
public class Client
{
public virtual int Id { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual string Url { get; set; }
}
And my interceptor
public class ClientInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if (entity is Client)
{
/*
manually add the COM_HOLD column to the Client entity
*/
List<string> pn_list = propertyNames.ToList();
pn_list.Add("COM_HOLD");
propertyNames = pn_list.ToArray();
List<Object> _state = state.ToList();
_state.Add(false);
state = _state.ToArray();
//somehow add an IType to types param ??
}
return base.OnSave(entity, id, state, propertyNames, types);
}
}
Does anyone have any ideas on how to do this properly?
I can't say for sure since I've never actually done this (like Stefan, I also prefer to just add a private property), but can you just add a NHibernate.Type.BooleanType to the types array?
List<IType> typeList = types.ToList();
typeList.Add(new BooleanType());
types = typesList.ToArray();
EDIT
Yes, it looks like you are right; the types have an internal constructor. I did some digging and found TypeFactory:
Applications should use static
methods and constants on
NHibernate.NHibernateUtil if the
default IType is good enough. For example, the TypeFactory should only
be used when the String needs to have a length of 300 instead of 255. At this point
NHibernate.String does not get you thecorrect IType. Instead use TypeFactory.GetString(300) and keep a
local variable that holds a reference to the IType.
So it looks like what you want is NHibernateUtil:
Provides access to the full range of
NHibernate built-in types. IType
instances may be used to bind values
to query parameters. Also a factory
for new Blobs and Clobs.
typeList.Add(NHibernateUtil.Boolean);
Personally I wouldn't do it so complicated. I would add the private property and assign it a default value - finished. You could also consider a default value in the database, then you don't need to do anything else.
private virtual bool COM_HOLD
{
get { return false; }
set { /* make NH happy */ }
}
Before writing a interceptor for that I would consider to write a database trigger. Because with the Interceptor you are "polluting" your data access layer. It could make it unstable and you could have strange problems.