I've been trying to map a clob field using Fluent NHibernate 1.2.0.712 against Oracle 10g. I'm using System.Data provider as it's available by default and was trying to avoid adding reference to ODP.Net due to previous client issues.
However, when I try to insert entities with mapped clob properties, I get the error:
ORA-01461: can bind a LONG value only for insert into a LONG column
I've tried to fix this by using the below convention, and decorating the appropriate property with [StringLength(4000)]:
public class StringLengthConvention : AttributePropertyConvention<StringLengthAttribute>
{
protected override void Apply(StringLengthAttribute attribute, IPropertyInstance instance)
{
instance.Length(attribute.MaximumLength);
}
}
This didn't work.
Then I tried the below using "TEXT", "CLOB" and "clob" values. Neither worked:
public class plaparteMappingOverride : IAutoMappingOverride<plaparte>
{
public void Override(AutoMapping<plaparte> mapping)
{
Map(x => x.disposiciones).CustomSqlTypeIs("TEXT");
}
}
Does anyone have further suggestions for this fix other than adding ODP as the provider?
For future reference: this post perfectly describes what causes this error and how you can solve it.
ORA-01461: can bind a LONG value only for insert into a LONG column
This error is not very helpful and goggling it will most likely result
in topics regarding oracle patches and the like. In reality this is a
bug with the microsoft oracle client driver. The driver mistakenly
infers the column type of the string being saved, and tries forcing
the server to update a LONG value into a CLOB/NCLOB column type. The
reason for the incorrect behavior is even more obscure and only
happens when all the following conditions are met.
when we set the IDbDataParameter.Value = (string whose length is : 4000 > length > 2000 )
when we set the IDbDataParameter.DbType = DbType.String
when DB Column is of type NCLOB/CLOB
Unfortunately NHibernate 2.0's default behavior is to do exactly the
above, making it quite more likely to run into this ugly bug when
using nhibernate and oracle.
Solution offered in the blog post: a custom NHibernate Oracle Driver:
/// <summary>
/// Initializes the parameter.
/// </summary>
/// <param name="dbParam">The db param.
/// <param name="name">The name.
/// <param name="sqlType">Type of the SQL.
protected override void InitializeParameter(System.Data.IDbDataParameter dbParam, string name, global::NHibernate.SqlTypes.SqlType sqlType)
{
base.InitializeParameter(dbParam, name, sqlType);
//System.Data.OracleClient.dll driver generates an exception
//we set the IDbDataParameter.Value = (string whose length: 4000 > length > 2000 )
//when we set the IDbDataParameter.DbType = DbType.String
//when DB Column is of type NCLOB/CLOB
//The Above is the default behavior for NHibernate.OracleClientDriver
//So we use the built-in StringClobSqlType to tell the driver to use the NClob Oracle type
//This will work for both NCLOB/CLOBs without issues.
//Mapping file will need to be update to use StringClob as the property type
if ((sqlType is StringClobSqlType))
{
((OracleParameter)dbParam).OracleType = OracleType.NClob;
}
}
Related
I am having problems with using OptimisticLock as a Convention.
However, using OptimisticLock within Individual ClassMap's works fine. It throws Stale State Object Exceptions.
Each Class corresponding to a Table in the database has a property (which corresponds to a Column in the Table) of type DateTime which I am trying to use for Locking using OptimisticLock.Version().
It works only when I use it within every ClassMap, I don't want to write so many ClassMaps, I instead want to use Auto Mapping.
It WORKS like this within the Class Map
Version(x => x.UpdTs).Column("UPD_TS");
OptimisticLock.Version();
So, I started using Convention below, but it DOESN'T WORK.
OptimisticLock.IsAny(x => x.Version());
I tried setting the DynamicUpdate, etc. Nothing seems to work for me.
Please help !
Here's what I did to get it work using a Convention :
/// <summary>
/// Class represents the Convention which defines which Property/Column serves as a part of the Optimistic Locking Mechanism.
/// </summary>
public class VersionConvention : IVersionConvention, IVersionConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IVersionInspector> criteria)
{
criteria.Expect(x => x.Name == "%COLUMN_NAME%");
}
/// <summary>
/// Method applies additional overrides to the <see cref="IVersionInstance"/>
/// </summary>
/// <param name="instance"><see cref="IVersionInstance"/></param>
public void Apply(IVersionInstance instance)
{
instance.Column("%COLUMN_NAME%");
}
}
%COLUMN_NAME% above is the Property being used for Locking using Version.
Then specified that the Version should be used for Optimistic Locking, when creating a FluentConfiguration Object, like this
OptimisticLock.Is(x => x.Version();
I need to use Fluent-nHibernate against a table with a composite primary key (Azure Table, primary keys being (PartitionKey,RowKey) and I would like to map them with corresponding properties on the entity (or with a component property, if easier)
my table would look like:
{
PartitionKey PK,
RowKey PK,
[..]
}
and the entity
public class MyRecord
{
public virtual string PartitionKey{get;set;}
public virtual string RowKey{get;set;}
[...]
}
My current projet uses a custom nHibernate Driver targeting AzureTable.
I managed to make it work with ClassMap or XML mappings. Therefore I am sure that the driver is working. Furthermore, the azure table HTTP requests are correct using classmaps or XML declarations.
However I really need conventions, so this isn't an acceptable solution.
Finally, there is always the option to map only RowKey as a PK, even if the Datastore use (PartitionKey,RowKey). It works too, However it's not really satisfying as it introduces an unicity handling mismatch between nHibernate and the underlying datastore.
UPDATE:
I tried to build a custom IIdentityConvention. The IIdentityInstance.Column() method takes into account only the first call.
However, if I use reflection to add both columns to the underlying mapping field, the configuration build fails with an XML validation exception (attribute 'class' required)
I got it working today, but it's not pretty. It also doesn't use a convention. As I understand conventions, they're really meant for tweaking things after the main mapping has occurred. Adding mappings I believe is considered out of scope for conventions.
In my project I have a generic automapping-based initialization procedure that knows nothing of types, but has dependency-injected mapping overrides for composite keys. Not exactly your scenario, but it's a similar problem.
The way I got this to work through reflection was to get hold of the appropriate AutoPersistenceModel object. If you have code looking like this:
Fluently.Configure().Mappings(m => ...
The AutoPersistenceModel object would be m.AutoMappings.First()
From here, it's pretty serious reflection work, culminating in a call to a protected method inside FluentNHibernate. Here's the code I'm using:
private void Override(AutoPersistenceModel container,
Type type,
IEnumerable<KeyValuePair<string,string>> compositeKeys)
{
// We need to call container.Override<T>(Action<Automapping<T>> populateMap)
// Through reflection...yikes
var overrideMethod = typeof(AutoPersistenceModel)
.GetMethod("Override")
.MakeGenericMethod(type);
var actionFactoryMethod = typeof(FluentNHibernateInitializer)
.GetMethod("CompositeMapperFactory",
BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(type);
var actionMethod = actionFactoryMethod
.Invoke(this, new object[] { compositeKeys });
overrideMethod.Invoke(container, new object[] {actionMethod});
}
private Action<AutoMapping<T>> CompositeMapperFactory<T>
(IEnumerable<KeyValuePair<string, string>> compositeKeys)
{
return new Action<AutoMapping<T>>(m =>
{
var compositeId = m.CompositeId();
foreach (var kvp in compositeKeys)
compositeId =
AddKeyProperty(
compositeId,
typeof(T).GetProperty(kvp.Key),
kvp.Value);
}
);
}
/// <summary>
/// Uses reflection to invoke private and protected members!
/// </summary>
/// <param name="compositeId"></param>
/// <param name="propertyInfo"></param>
/// <returns></returns>
private CompositeIdentityPart<T> AddKeyProperty<T>
(CompositeIdentityPart<T> compositeId,
PropertyInfo propertyInfo,
string column)
{
var member = FluentNHibernate.MemberExtensions.ToMember(propertyInfo);
var keyPropertyMethod = typeof(CompositeIdentityPart<T>)
.GetMethod("KeyProperty",
BindingFlags.Instance | BindingFlags.NonPublic);
return (CompositeIdentityPart<T>)
keyPropertyMethod
.Invoke(compositeId, new object[] { member, column, null });
}
I have a problem with NHibernate, I can't seem to find any solution for.
In my project I have a simple entity (Batch), but whenever I try and run the following test, I get an exception.
I've triede a couple of different ways to perform a similar query, but almost identical exception for all (it differs in which LINQ method being executed).
The first test:
[Test]
public void QueryLatestBatch()
{
using (var session = SessionManager.OpenSession())
{
var batch = session.Query<Batch>()
.FirstOrDefault();
Assert.That(batch, Is.Not.Null);
}
}
The exception:
System.NullReferenceException : Object reference not set to an instance of an object.
at NHibernate.Linq.NhQueryProvider.PrepareQuery(Expression expression, ref IQuery query, ref NhLinqExpression nhQuery)
at NHibernate.Linq.NhQueryProvider.Execute(Expression expression)
at System.Linq.Queryable.FirstOrDefault(IQueryable`1 source)
The second test:
[Test]
public void QueryLatestBatch2()
{
using (var session = SessionManager.OpenSession())
{
var batch = session.Query<Batch>()
.OrderBy(x => x.Executed)
.Take(1)
.SingleOrDefault();
Assert.That(batch, Is.Not.Null);
}
}
The exception:
System.NullReferenceException : Object reference not set to an instance of an object.
at NHibernate.Linq.NhQueryProvider.PrepareQuery(Expression expression, ref IQuery query, ref NhLinqExpression nhQuery)
at NHibernate.Linq.NhQueryProvider.Execute(Expression expression)
at System.Linq.Queryable.SingleOrDefault(IQueryable`1 source)
However, this one is passing (using QueryOver<>):
[Test]
public void QueryOverLatestBatch()
{
using (var session = SessionManager.OpenSession())
{
var batch = session.QueryOver<Batch>()
.OrderBy(x => x.Executed).Asc
.Take(1)
.SingleOrDefault();
Assert.That(batch, Is.Not.Null);
Assert.That(batch.Executed, Is.LessThan(DateTime.Now));
}
}
Using the QueryOver<> API is not bad at all, but I'm just kind of baffled that the Query<> API isn't working, which is kind of sad, since the First() operation is very concise, and our developers really enjoy LINQ.
I really hope there is a solution to this, as it seems strange if these methods are failing such a simple test.
EDIT
I'm using Oracle 11g, my mappings are done with FluentNHibernate registered through Castle Windsor with the NHibernate Facility.
As I wrote, the odd thing is that the query works perfectly with the QueryOver<> API, but not through LINQ.
There is an issue with the current implementation of the LINQ extensionmethods for NHibernate 3.1.0.4000 used together with NHibernate Facility 2.0RC (and previous versions) (see: https://nhibernate.jira.com/browse/NH-2626 and discussion here: http://groups.google.com/group/castle-project-devel/browse_thread/thread/ac90148a8d4c8477)
The fix I am using at the moment is to simply ignore the LINQ extensionmethods provided by NHibernate and create it myself. They're really just one-liners:
public static class NHibernateLinqExtensions
{
/// <summary>
/// Performs a LINQ query on the specified type.
/// </summary>
/// <typeparam name="T">The type to perform the query on.</typeparam>
/// <param name="session"></param>
/// <returns>A new <see cref="IQueryable{T}"/>.</returns>
/// <remarks>This method is provided as a workaround for the current bug in the NHibernate LINQ extension methods.</remarks>
public static IQueryable<T> Linq<T>(this ISession session)
{
return new NhQueryable<T>(session.GetSessionImplementation());
}
/// <summary>
/// Performs a LINQ query on the specified type.
/// </summary>
/// <typeparam name="T">The type to perform the query on.</typeparam>
/// <param name="session"></param>
/// <returns>A new <see cref="IQueryable{T}"/>.</returns>
/// <remarks>This method is provided as a workaround for the current bug in the NHibernate LINQ extension methods.</remarks>
public static IQueryable<T> Linq<T>(this IStatelessSession session)
{
return new NhQueryable<T>(session.GetSessionImplementation());
}
}
Then, when I need to do a LINQ query, I just use session.Linq<EntityType>() instead of session.Query<EntityType>.
Hope it helps someone in the same situation that I was.
I found the following: http://groups.google.com/group/castle-project-users/browse_thread/thread/5efc9f3b7b5d6a08
Apparently there is an issue with the current version of the NHibernate Facility and NHibernate 3.1.0.4000.
I guess I'll just have to wait for a fix :)
Background
I am using a legacy database with all kinds of ugly corners. One bit is auditing. There is a table listing tablename/field combinations of fields that should have an audit trail. For example, if there is a row that has "WORKORDER" for the table name and "STATUS" for the fieldname, then I need to add row(s) to the auditing table whenever the Workorder.Status property changes in the application. I know the approach: NH events or interceptors, but I've got an issue to figure out before I get to that stage.
Question
What I need to know is how to get a list of key/value pairs for a single persistent class containing (a) the database field name and (b) the associated property name in the class. So for my example, I have a class called Workorder associated with a table called (no surprise) WORKORDER. I have a property on that Workorder class called CurrentStatus. The matching property in the WORKORDER table is STATUS. Notice the mismatch between the property name and table column name? I need to know the property name to access the before and after data for the audit. But I also need to know the backing column name so that I can query the stupid legacy "AuditTheseColumns" table.
What I've tried
in my application I change the Workorder.CurrentStatus from "TS" to "IP". I look in my audit tracking table and see that the WORKORDER.STATUS column is tracked. So after calling Session.SaveOrUpdate(workorder), I need to find the Workorder property associated with the STATUS column and do a Session.Save(auditRecord) telling it the old ("TS") and new ("IP") values.
As far as I can tell, you can get information about the class:
var fieldNames = new List<string>();
IClassMetadata classMetadata = SessionFactory(Resources.CityworksDatasource).GetClassMetadata(typeof(T));
int propertyCount = 0;
foreach (IType propertyType in classMetadata.PropertyTypes)
{
if (propertyType.IsComponentType)
{
var cp = (ComponentType)propertyType;
foreach (string propertyName in cp.PropertyNames)
{
fieldNames.Add(propertyName);
}
}
else if(!propertyType.IsCollectionType)
{
fieldNames.Add(classMetadata.PropertyNames[propertyCount + 1]);
}
propertyCount++;
}
And information about the table:
var columnNames = new List<string>();
PersistentClass mappingMeta = ConfigureCityworks().GetClassMapping(typeof(T));
foreach (Property property in mappingMeta.PropertyIterator)
{
foreach (Column selectable in property.ColumnIterator)
{
if (columnNames.Contains(selectable.Name)) continue;
columnNames.Add(selectable.Name);
}
}
But not at the same time. Any ideas? I'm at a loss where to look next.
How to get the database column / field names and class property names for an entity mapped by NHibernate:
using System;
using System.Collections.Generic;
using System.Reflection;
using NHibernate;
using NHibernate.Persister.Entity;
namespace Stackoverflow.Example
{
/// <summary>
/// NHibernate helper class
/// </summary>
/// <remarks>
/// Assumes you are using NHibernate version 3.1.0.4000 or greater (Not tested on previous versions)
/// </remarks>
public class NHibernateHelper
{
/// <summary>
/// Creates a dictionary of property and database column/field name given an
/// NHibernate mapped entity
/// </summary>
/// <remarks>
/// This method uses reflection to obtain an NHibernate internal private dictionary.
/// This is the easiest method I know that will also work with entitys that have mapped components.
/// </remarks>
/// <param name="sessionFactory">NHibernate SessionFactory</param>
/// <param name="entity">An mapped entity</param>
/// <returns>Entity Property/Database column dictionary</returns>
public static Dictionary<string, string> GetPropertyAndColumnNames(ISessionFactory sessionFactory, object entity)
{
// Get the objects type
Type type = entity.GetType();
// Get the entity's NHibernate metadata
var metaData = sessionFactory.GetClassMetadata(type.ToString());
// Gets the entity's persister
var persister = (AbstractEntityPersister)metaData;
// Creating our own Dictionary<Entity property name, Database column/filed name>()
var d = new Dictionary<string, string>();
// Get the entity's identifier
string entityIdentifier = metaData.IdentifierPropertyName;
// Get the database identifier
// Note: We are only getting the first key column.
// Adjust this code to your needs if you are using composite keys!
string databaseIdentifier = persister.KeyColumnNames[0];
// Adding the identifier as the first entry
d.Add(entityIdentifier, databaseIdentifier);
// Using reflection to get a private field on the AbstractEntityPersister class
var fieldInfo = typeof(AbstractEntityPersister)
.GetField("subclassPropertyColumnNames", BindingFlags.NonPublic | BindingFlags.Instance);
// This internal NHibernate dictionary contains the entity property name as a key and
// database column/field name as the value
var pairs = (Dictionary<string, string[]>)fieldInfo.GetValue(persister);
foreach (var pair in pairs)
{
if (pair.Value.Length > 0)
{
// The database identifier typically appears more than once in the NHibernate dictionary
// so we are just filtering it out since we have already added it to our own dictionary
if (pair.Value[0] == databaseIdentifier)
break;
d.Add(pair.Key, pair.Value[0]);
}
}
return d;
}
}
}
Usage:
// Get your NHiberate SessionFactory wherever that is in your application
var sessionFactory = NHibernateHelper.SessionFactory;
// Get an entity that you know is mapped by NHibernate
var customer = new Customer();
// Get a dictionary of the database column / field names and their corresponding entity property names
var propertyAndColumnNamesDictionary =
Stackoverflow.Example.NHibernateHelper.GetPropertyAndColumnNames(sessionFactory, customer);
Now if I understand correctly here is what you could do....
One way would be to read and parse the XML mapping files from the dll that are embedded before or even after the NHibernate session factory is build. This way you can get all the info you need from the XML files (with column corresponds to which property) and populate a global (probably static) collection of custom objects that will hold the entity's name and a dictionary with key the propery name and value the column name (or the other way around).
You can then access this global collection to get the info you need right after the call to SaveOrUpdate() as you described it.
The downside of this approach is that you need to write your own XML parsing logic to retrive the info you need from the XML mapping files.
An alternative would be to create a custom attribute to decorate each property of your entities in order to get the column name that corresponds to each property.
An example would be:
[ColumnName("MyColumn")]
public string Status { get; set; }
Using reflection you can easily get the property name and the from the attribute the column name that this property is mapped to.
The downside of this approach would be having to keep in sync your column names with the attribute values when the database schema is updated.
I have created a User Defined Type in .Net 3.5 as per my blog entry at :
http://jwsadlerdesign.blogspot.com/2009/04/this-is-how-you-register.html
This works fine when using SQL with technologies like nHibernate.
However, when I try to map my LinQ to SQL class to use this UDT (with attribute defintions not XML), and I setup the property as the enumeration. I cannot get LinQ to map to this type. I have tried Image, Binary, varchar and integer all of which seem to issue Invalid Cast errors.
In particular I get the error 'Unable to cast object of type 'ISTD.InstallManager.Common.Classes.SQLUDTTargetType' to type 'System.Byte[]' any ideas or help would be much appreciated.
James.
UPDATE: I ran into this myself recently and found that the previous solution wasn't quite complete. Despite what all of the documentation says, it is possible to do this, but somewhat painful.
The first step, for your own convenience, is to implement some conversion operators:
public class MyUDT : INullable, IBinarySerialize
{
// Class implementation would go here
// ...
public static explicit operator MyUDT(byte[] data)
{
using (MemoryStream stream = new MemoryStream(data))
{
using (BinaryReader reader = new BinaryReader(stream))
{
MyUDT result = new MyUDT();
result.Read(reader);
return result;
}
}
}
public static explicit operator byte[](MyUDT x)
{
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(ms))
{
x.Write(writer);
}
return ms.ToArray();
}
}
}
Linq to SQL will still flat-out refuse to give you the UDT field, no matter how you declare the property. So you have to give it a binary field instead. You don't need a stored procedure or any custom SQL for this, just add a computed column to your table:
ALTER TABLE MyTable
ADD UDTField_Data AS CAST(UDTField AS varbinary(len))
Where len is whatever your UDT defines in the MaxByteSize attribute.
Now you can finally get access to the column data. You might be tempted to use your UDT as the return type of the new property, thinking that Linq to SQL will find your conversion operator and automatically convert from the byte array; don't bother. Linq to SQL will decide that it's actually a serialized .NET object and spit out a message to the effect of "input stream is not a valid binary format." Instead, you need another layer of indirection:
private MyUDT udtField;
[Column(Name = "UDTField_Data", DbType = "varbinary(len)")]
private byte[] UdtFieldData
{
get { return (byte[])udtField; }
set { udtField = (MyUDT)value; }
}
public MyUDT UdtProperty
{
get { return udtField; }
set { udtField = value; }
}
A few notes to make it clear what's going on here:
The actual field data (udtField) is declared as the UDT itself, not a byte array. The reason for this is that we only want the conversion to happen when loading from or saving to the database. If you had to convert the byte array to the UDT every time you accessed it, it would not only hurt performance, but it would cause inconsistencies if the UDT declares any mutable fields.
The raw byte[] property (UdtFieldData) is declared private, so consumers only see the UDT itself. Linq to SQL will still read it as long as it has the [Column] attribute.
The UdtFieldData property does not declare a storage property. This is critical; if you try to use the UDT field as the storage property, you'll just get the same type conversion error.
Finally, the UdtProperty property is how consumers actually get to access the data. To them it looks like any other property.
It's unfortunate that you have to jump through so many hoops to get this to work, but it does work. You'll probably have difficulties doing this kind of massaging through the Linq surface designer, which is just one of several reasons why I don't use it; better to write the classes yourself and use SqlMetal to help you along if necessary.