I have a tablestructure like this:
Table entity
(
otherEntity_id int // primarykey
id int // primarykey
parent_id int
)
and class
public class Entity
{
public OtherEntity Other { get; set; }
// simple int to discriminate different Entity for the same OtherEntity
public int Id { get; set; }
public Entity Parent { get; set; }
}
is it possible to map this to the class Entity? (if yes how)
the following throws on save, because there are not enough columns in the dbcommand, one is used twice:
CompositeId()
.KeyReference(e => e.Other, "otherEntity_id")
.KeyProperty(e => e.Id, "id")
References(e => e.Parent).Columns("otherEntity_id", "parent_id");
It doesnt matter using xml or fluent.
Edit:
without the reference mapped no error, with reference mapped following error
(i had this error several times, everytime when i have more values than mapped columns):
System.ArgumentOutOfRangeException: System.ArgumentOutOfRangeException : Der Index lag außerhalb des Bereichs. Er muss nicht negativ und kleiner als die Auflistung sein.
Parametername: index
bei System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
bei System.ThrowHelper.ThrowArgumentOutOfRangeException()
bei System.Collections.Generic.List`1.get_Item(Int32 index)
bei Npgsql.NpgsqlParameterCollection.get_Item(Int32 index)
bei Npgsql.NpgsqlParameterCollection.GetParameter(Int32 index)
bei System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index)
bei NHibernate.Type.Int16Type.Set(IDbCommand rs, Object value, Int32 index)
bei NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int32 index)
bei NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int32 index, ISessionImplementor session)
bei NHibernate.Type.ComponentType.NullSafeSet(IDbCommand st, Object value, Int32 begin, ISessionImplementor session)
bei NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index)
bei NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
bei NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
bei NHibernate.Action.EntityInsertAction.Execute()
bei NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
bei NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
bei NHibernate.Engine.ActionQueue.ExecuteActions()
bei NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
bei NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
bei NHibernate.Impl.SessionImpl.Flush()
testcode
var e1 = new Entity { Id = 2, Other = other };
var e2 = new Entity { Id = 3, Other = other, Parent = e1 };
Session.Save(e1);
Session.Save(e2);
Session.Flush(); // throws here
Session.Clear();
var key = new Entity { Id = e2.Id, Other = e2.Other };
var loaded = Session.Get<Entity>(key);
Edit:
if it's not possible could please someone tell me?
If you are referencing the same column above twice (otherEntity_id) I've seen mappings like this to avoid this sort of error:
References(e => e.Parent).Columns("otherEntity_id", "parent_id")
.Not.Update()
.Not.Insert();
Also when you are running into a problem it is always helpful to show the full error message you are running into.
I still haven't found a solution and mapped changed my model to:
public class Entity
{
public OtherEntity Other { get; set; }
// simple int to discriminate different Entity for the same OtherEntity
public int Id { get; set; }
public int ParentId { get; set; }
}
Related
I use the IClientMessageInspector and it's method BeforeSendRequest, to inspect the request messages. The thing is that some of request messages has members, which members are of type object (in the real scenario thre may be in example string or some other value, which is specified in the KnownType attribute):
[DataContract(Namespace = "")]
[KnownType(typeof(SomeValueDTO))]
public class CenDTO
{
[DataMember]
public string Uri { get; set; }
[DataMember]
public object Value { get; set; }
}
Notice, that in this project the namespaces are not defined anywhere. So on the "BeforeSendRequest" I got exception. And looks like this interface uses not the same DataContractSerializer as in the real service, which hasn't such serialization errors or smth like that.
The error:
The empty string '' is not a valid name.
at System.Xml.XmlTextWriter.LookupPrefix(String ns)
at System.Xml.XmlDictionaryWriter.XmlWrappedWriter.LookupPrefix(String namespaceUri)
at System.Xml.XmlDictionaryWriter.XmlWrappedWriter.WriteXmlnsAttribute(String prefix, String namespaceUri)
at System.Xml.XmlDictionaryWriter.WriteXmlnsAttribute(String prefix, XmlDictionaryString namespaceUri)
at System.Runtime.Serialization.XmlWriterDelegator.WriteXmlnsAttribute(XmlDictionaryString ns)
at System.Runtime.Serialization.XmlWriterDelegator.WriteAttributeQualifiedName(String attrPrefix, XmlDictionaryString attrName, XmlDictionaryString attrNs, XmlDictionaryString name, XmlDictionaryString ns)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryString dataContractName, XmlDictionaryString dataContractNamespace)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
at WriteCenDTOToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.ToString()
at CID.TopicAnalyst.CIA.Test.Utils.MessageInspector.BeforeSendRequest(Message& request, IClientChannel channel) in D:\git\...\MessageInspector.cs:line 25
at System.ServiceModel.Dispatcher.ImmutableClientRuntime.BeforeSendRequest(ProxyRpc& rpc)
Implementation of "BeforeSendRequest"(The crash is on the .ToString() ):
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if(request.Headers.Count > 0)
{
request.Headers.RemoveAt(0);
}
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
var message = buffer.CreateMessage().ToString();
var resultFile = ConfigurationManager.AppSettings.GetValues("requestFile").First();
_serializer.Write(resultFile, message);
return null;
}
The testing itself is a separate solution, which uses the service's reference.
On the service imnplmentation the operation looks like this:
[ServiceContract]
public interface ISoapService
{
[OperationContract]
SomeListResponseMessage GetSomeList(SomeListRequestMessage request);
}
[MessageContract]
public class SomeListRequestMessage : BaseRequestMessage
{
...
[MessageBodyMember]
public CenDTO Cen { get; set; }
...
}
[DataContract(Namespace = "")]
[KnownType(typeof(SemValueDTO))]
public class CenDTO
{
...
[DataMember]
public object Value { get; set; }
}
Some code of the Testing Solution:
static class Program
{
static void Main(string[] args)
{
while(true)
{
var client = new SoapServiceClient("BasicHttpBinding_ISoapService");
client.Endpoint.Behaviors.Add(new InspectorBehavior());
var soapService = client.ChannelFactory.CreateChannel();
var co = new CooRequestCreator(soapService);
co.CreateAndWriteRequests();
Console.WriteLine("Press y to repeat");
var str = Console.ReadLine();
if(str != "y") break;
}
}
Constructing the request message before calling the service:
class CooRequestCreator : BaseRequestCreator<SomeListRequestMessage>
{
SemValueDTO _semaValue = new SemValueDTO
{
Key = "fb",
Label = "Guild",
Type = "org"
};
public CooRequestCreator(ISoapService soapService) : base(soapService)
{
}
protected override SomeListRequestMessage CreateMinRequest()
{
return new SomeListRequestMessage
{
Cen =
new CenDTO
{
Value = _semaValue,
},
MaxCount = 5
};
}
Maybe someone can has the same experience and can help with that?
i have the following classes and mappings
abstract class BaseClass
{
public virtual int Keypart1 { get; set; }
public virtual int Keypart2 { get; set; }
// overridden Equals() and GetHashCode()
}
class InheritingClass : BaseClass
{
}
class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap()
{
CompositeId()
.KeyProperty(x => x.Keypart1)
.KeyProperty(x => x.Keypart2);
}
}
class InheritingClassMap : SubclassMap<InheritingClass>
{
public InheritingClassMap()
{
KeyColumn("Keypart1");
KeyColumn("Keypart2");
}
}
inserting, updating and session.Get() work fine but querying like
var result = session.CreateCriteria<InheritingClass>().List<InheritingClass>();
throws
NHibernate.InstantiationException: Cannot instantiate abstract class or interface: ConsoleApplication1.BaseClass
bei NHibernate.Tuple.PocoInstantiator.Instantiate()
bei NHibernate.Tuple.Component.AbstractComponentTuplizer.Instantiate()
bei NHibernate.Type.ComponentType.Instantiate(EntityMode entityMode)
bei NHibernate.Type.ComponentType.Instantiate(Object parent, ISessionImplementor session)
bei NHibernate.Type.EmbeddedComponentType.Instantiate(Object parent, ISessionImplementor session)
bei NHibernate.Type.ComponentType.ResolveIdentifier(Object value, ISessionImplementor session, Object owner)
bei NHibernate.Type.ComponentType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner)
bei NHibernate.Loader.Loader.GetKeyFromResultSet(Int32 i, IEntityPersister persister, Object id, IDataReader rs, ISessionImplementor session)
bei NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies)
...
it seems NH tries to instantiate the abstract baseclass as a compositekey and fails. Can i somehow work around that?
UPDATE: my testcode
var config = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql().FormatSql())
.Mappings(m => m.FluentMappings
.Add<BaseClassMap>()
.Add<InheritingClassMap>()
)
.BuildConfiguration();
var sf = config.BuildSessionFactory();
using (var session = sf.OpenSession())
{
new SchemaExport(config).Execute(false, true, false, session.Connection, null);
var obj = new InheritingClass
{
Keypart1 = 1,
Keypart2 = 2,
};
session.Save(obj);
session.Flush();
session.Clear();
// throws here
var result = session.CreateCriteria<InheritingClass>().List<InheritingClass>();
}
How does your database look like? According to your mapping, you are using a table-per-subclass mapping. In this case NHibernate will try to create an instance of BaseClass if no row is found in the table of InheritingClass.
edit: There is a abstract="true" attribute for a <class> in NHibernate mapping which might fix your problem. But it seems like this isn't exposed in Fluent NHibernate for a ClassMap, only for SubclassMap (which won't help you).
But maybe you could also fix the problem by using a component for the composite ID (so that NHibernate doesn't need to create a BaseClass object for its EntityKey. See here for infos about that.
thx to cremor this is what i end up with
abstract class BaseClass
{
public virtual BaseClassId Key { get; set; }
}
class BaseClassId
{
public virtual int Keypart1 { get; set; }
public virtual int Keypart2 { get; set; }
public override bool Equals(object obj)
{
var other = obj as BaseClassId;
return (other != null) && (Keypart1 == other.Keypart1) && (Keypart2 == other.Keypart2);
}
public override int GetHashCode()
{
unchecked
{
return Keypart1 + Keypart2;
}
}
}
// mapping
CompositeId(b => b.Key)
.KeyProperty(x => x.Keypart1)
.KeyProperty(x => x.Keypart2);
var obj = new InheritingClass
{
Key = new BaseClassId
{
Keypart1 = 1,
Keypart2 = 2,
}
};
I am using the TIME datatype from SQL Server 2008 and I am having some problems getting it to work with NHibernate.
public TimeTableEventMap()
{
Id(x => x.Id)
Map(x => x.Day).NvarcharWithMaxSize().Not.Nullable();
Map(x => x.StartTime).Length(4).TimeDataType().Not.Nullable();
Map(x => x.Endtime).Length(4).TimeDataType().Not.Nullable();
References(x => x.TimeTable).Not.Nullable().Cascade.All();
References(x => x.RequiredSettings).Not.Nullable().Cascade.All();
}
/// <summary>
/// MS Sql 2008 date type.
/// </summary>
/// <param name="map"></param>
/// <returns></returns>
public static PropertyPart TimeDataType(this PropertyPart map)
{
return map.CustomSqlType("time");
}
public class TimeTableEvent
{
public virtual int Id { get; private set; }
public virtual DayOfWeek Day { get; set; }
public virtual DateTime StartTime { get; set; }
public virtual DateTime Endtime { get; set; }
public virtual TimeTable TimeTable { get; set; }
public virtual RequiredSetting RequiredSettings { get; set; }
}
I get this error
NHibernate.Exceptions.GenericADOException was caught
Message=could not execute query
[ SELECT TOP (#p0) this_.TimeTableEventId as TimeTabl1_15_1_,
this_.Day as Day15_1_, this_.StartTime as StartTime15_1_,
this_.Endtime as Endtime15_1_, this_.TimeTableId as TimeTabl5_15_1_,
this_.RequiredSettingsId as Required6_15_1_,
requiredse2_.RequiredSettingsId as Required1_10_0_,
requiredse2_.BackgroundColor as Backgrou2_10_0_, requiredse2_.Title as
Title10_0_ FROM TimeTableEvents this_ inner join RequiredSettings
requiredse2_ on
this_.RequiredSettingsId=requiredse2_.RequiredSettingsId WHERE
this_.TimeTableId in (#p1) ]
Positional parameters: #0>14
[SQL: SELECT TOP (#p0) this_.TimeTableEventId as TimeTabl1_15_1_,
this_.Day as Day15_1_, this_.StartTime as StartTime15_1_,
this_.Endtime as Endtime15_1_, this_.TimeTableId as TimeTabl5_15_1_,
this_.RequiredSettingsId as Required6_15_1_,
requiredse2_.RequiredSettingsId as Required1_10_0_,
requiredse2_.BackgroundColor as Backgrou2_10_0_, requiredse2_.Title as
Title10_0_ FROM TimeTableEvents this_ inner join RequiredSettings
requiredse2_ on
this_.RequiredSettingsId=requiredse2_.RequiredSettingsId WHERE
this_.TimeTableId in (#p1)]
Source=NHibernate
SqlString=SELECT TOP (#p0) this_.TimeTableEventId as
TimeTabl1_15_1_, this_.Day as Day15_1_, this_.StartTime as
StartTime15_1_, this_.Endtime as Endtime15_1_, this_.TimeTableId as
TimeTabl5_15_1_, this_.RequiredSettingsId as Required6_15_1_,
requiredse2_.RequiredSettingsId as Required1_10_0_,
requiredse2_.BackgroundColor as Backgrou2_10_0_, requiredse2_.Title as
Title10_0_ FROM TimeTableEvents this_ inner join RequiredSettings
requiredse2_ on
this_.RequiredSettingsId=requiredse2_.RequiredSettingsId WHERE
this_.TimeTableId in (#p1)
StackTrace:
at NHibernate.Loader.Loader.DoList(ISessionImplementor
session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor
session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.List(ISessionImplementor
session, QueryParameters queryParameters, ISet1 querySpaces, IType[]
resultTypes)
at NHibernate.Loader.Criteria.CriteriaLoader.List(ISessionImplementor
session)
at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria,
IList results)
at NHibernate.Impl.CriteriaImpl.List(IList results)
at NHibernate.Impl.CriteriaImpl.List[T]()
at NHibernate.Criterion.QueryOver1.ListU
at NHibernate.Criterion.QueryOver`1.NHibernate.IQueryOver.ListU
at TimeTableRepo.cs:line 47
at TimeTableService.cs:line 43
InnerException: System.FormatException
Message=Input string '16:00:00' was not in the correct
format.
Source=NHibernate
StackTrace:
at NHibernate.Type.DateTimeType.Get(IDataReader rs,
Int32 index)
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name)
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[]
names, ISessionImplementor session, Object owner)
at NHibernate.Type.AbstractType.Hydrate(IDataReader
rs, String[] names, ISessionImplementor session, Object owner)
at NHibernate.Persister.Entity.AbstractEntityPersister.Hydrate(IDataReader
rs, Object id, Object obj, ILoadable rootLoadable, String[][]
suffixedPropertyColumns, Boolean allProperties, ISessionImplementor
session)
at NHibernate.Loader.Loader.LoadFromResultSet(IDataReader rs, Int32 i,
Object obj, String instanceClass, EntityKey key, String rowIdAlias,
LockMode lockMode, ILoadable rootPersister, ISessionImplementor
session)
at NHibernate.Loader.Loader.InstanceNotYetLoaded(IDataReader dr, Int32 i,
ILoadable persister, EntityKey key, LockMode lockMode, String
rowIdAlias, EntityKey optionalObjectKey, Object optionalObject, IList
hydratedObjects, ISessionImplementor session)
at NHibernate.Loader.Loader.GetRow(IDataReader rs,
ILoadable[] persisters, EntityKey[] keys, Object optionalObject,
EntityKey optionalObjectKey, LockMode[] lockModes, IList
hydratedObjects, ISessionImplementor session)
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet,
ISessionImplementor session, QueryParameters queryParameters,
LockMode[] lockModeArray, EntityKey optionalObjectKey, IList
hydratedObjects, EntityKey[] keys, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session,
QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor
session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoList(ISessionImplementor
session, QueryParameters queryParameters)
InnerException: System.InvalidCastException
Message=Unable to cast object of type
'System.TimeSpan' to type 'System.IConvertible'.
Source=mscorlib
public List<TimeTableEvent> GetTimeTableEvents(Student student, List<int> timeTableIds)
{
TimeTableEvent tAlias = null;
List<TimeTableEvent> allEvents = session.QueryOver<TimeTableEvent>(() => tAlias)
.Where(Restrictions.In(Projections.Property(() => tAlias.TimeTable.Id), timeTableIds))
.Fetch(r => r.RequiredSettings).Eager
.TransformUsing(Transformers.DistinctRootEntity)
.Take(QueryLimits.TimeTableEvents)
.List<TimeTableEvent>().ToList();
return allEvents;
}
Date/Time Support in NHibernate
You should use the TimeSpan type on the .NET side of things instead of DateTime (since there is no Date) like Dotjoe said in his comment.
To put VahidN's answer explicitly, when you have an error message that says
Unable to cast object of type 'System.TimeSpan' to type 'System.IConvertible'.
It means the property you're trying to map to should be a TimeSpan instead of a DateTime. Same deal with DateTimeOffset.
A partial solution which I found useful is to use formula=CONVERT(DateTime, <ColumnName>) in the mapping.
The significant downside is that this is only useful for read-only access.
Given the following Fluent NHibernate maps:
public class FastTrackPackageClassMap : ClassMap<FastTrackPackage>
{
public FastTrackPackageClassMap()
{
Id(x => x.Id);
References(x => x.UserCreated, "UserIdCreated").Cascade.None();
References(x => x.UserSent, "UserIdSent").Nullable().Cascade.None();
HasMany(x => x.GetFileHeaders()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetEmailRecords()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetPaymentRecords()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetFileTrailers()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
Map(x => x.TestPackage);
Map(x => x.DateTimeCreated).Generated.Insert();
Map(x => x.DateTimeSent).Nullable();
}
}
public class FileHeaderFastTrackRecordClassMap : ClassMap<FileHeaderFastTrackRecord>
{
public FileHeaderFastTrackRecordClassMap()
{
Id(FileHeaderFastTrackRecord.Expressions.Id);
References(x => x.Package, "PackageId");
Map(x => x.FileDateTime);
Map(x => x.CustomerId);
Map(x => x.TestPackage, "TestIndicator").CustomType(typeof (TestPackageUserType));
Map(x => x.BankId);
}
}
... and the given custom user type:
public class TestPackageUserType : IUserType
{
public bool Equals(object x, object y)
{
if (x == null)
return false;
return x.Equals(y);
}
public int GetHashCode(object x) { return x.GetHashCode(); }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var testIndicator = (char) NHibernateUtil.Character.NullSafeGet(rs, names[0]);
return testIndicator == 'T';
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
NHibernateUtil.Character.NullSafeSet(cmd, null, index);
return;
}
if ((bool) value) { NHibernateUtil.Character.NullSafeSet(cmd, 'T', index); }
else { NHibernateUtil.Character.NullSafeSet(cmd, 'P', index); }
}
public object DeepCopy(object value)
{
if (value == null) return true;
return (bool) value;
}
public object Replace(object original, object target, object owner)
{
if (target != null && !(bool) target)
{
return 'P';
}
return 'T';
}
public object Assemble(object cached, object owner) { throw new NotImplementedException(); }
public object Disassemble(object value)
{
if (value != null)
{
var testIndicator = (char[]) value;
if (testIndicator[0] == 'P')
{
return false;
}
}
return true;
}
public SqlType[] SqlTypes { get { return new[] {new SqlType(DbType.String)}; } }
public Type ReturnedType { get { return typeof (bool); } }
public bool IsMutable { get; private set; }
}
I receive the following exception and stack when I Update Merge the FastTrackPackage with a child FileHeaderFastTrackRecord:
NHibernate.PropertyAccessException: Invalid Cast (check your mapping for property type mismatches); setter of BNYM_Extranet.Common.RemEDIFastTrack.FileHeaderFastTrackRecord
at NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values)
at NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValues(Object entity, Object[] values)
at NHibernate.Persister.Entity.AbstractEntityPersister.SetPropertyValues(Object obj, Object[] values, EntityMode entityMode)
at NHibernate.Event.Default.DefaultMergeEventListener.CopyValues(IEntityPersister persister, Object entity, Object target, ISessionImplementor source, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.EntityIsDetached(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event, IDictionary copyCache)
at NHibernate.Impl.SessionImpl.FireMerge(IDictionary copiedAlready, MergeEvent event)
at NHibernate.Impl.SessionImpl.Merge(String entityName, Object obj, IDictionary copiedAlready)
at NHibernate.Engine.CascadingAction.MergeCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeToOne(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeCollection(Object child, CascadeStyle style, Object anything, CollectionType type)
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
at NHibernate.Event.Default.DefaultMergeEventListener.CascadeOnMerge(IEventSource source, IEntityPersister persister, Object entity, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.EntityIsDetached(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event)
at NHibernate.Impl.SessionImpl.FireMerge(MergeEvent event)
at NHibernate.Impl.SessionImpl.Merge(String entityName, Object obj)
at NHibernate.Impl.SessionImpl.Merge(Object obj)
at DAL.NHibernate.RepositoryWithTypedId`2.Update(T entity) in Repository.cs: line 72
at UnitTests.DAL.NHibernate.RemEDIFastTrack.FastTrackPackageRepositoryFixture.Update() in FastTrackPackageRepositoryFixture.cs: line 290
When I Save the FastTrackPackage with a FileHeaderFastTrackRecord I receive no error and it saves properly. I can also Save and Update FileHeaderFastTrackRecords independently.
At this point I'm at a loss. I suspect the custom user type, but I can't be sure.
How can I diagnose or fix this problem?
It turns out the 'TestPackageUserType.Replace()' method was wrong. It was returning a char when it should be returning a bool and code further down the stack choked on it (providing the cryptic exception message). My new Replace() method looks like this:
public object Replace(object original, object target, object owner)
{
return original;
}
I'm mapping a objectified relationship (the many->many mapping table contains properties),
following this guide.
The SQL generated(see exception) is working and returns what i want(strictly speaking it should have been an inner join?).
But I get a GenericADOException saying:
could not initialize a collection: [Questionnaires.Core.Questionnaire.Questions#CBDEDAFC183B4CD7AF1422423A91F589][SQL: SELECT questions0_.ida_questionnaire_id as ida4_2_, questions0_.ida_questionnaire_question_id as ida1_2_, questions0_.ida_questionnaire_question_id as ida1_5_1_, questions0_.question_no as question2_5_1_, questions0_.ida_question_id as ida3_5_1_, questions0_.ida_questionnaire_id as ida4_5_1_, question1_.ida_question_id as ida1_3_0_, question1_.ida_question_type_id as ida2_3_0_, question1_.description as descript3_3_0_, question1_.validate_max as validate4_3_0_, question1_.validate_min as validate5_3_0_ FROM ida_questionnaire_question questions0_ left outer join ida_question question1_ on questions0_.ida_question_id=question1_.ida_question_id WHERE questions0_.ida_questionnaire_id=?]
I'm guessing this is because Questionnaire is really mapping to QuestionnaireQuestion not Question(Questionnaire->hasMany->QuestionnaireQuestion<-hasMany<-Question). But I can't seem to find a way of going around this.
Question:
public class Question : PersistentObjectWithTypedId<string>
{
#region Constructors
public Question()
{
Alternatives = new List<Alternative>();
Questionnaires = new List<Questionnaire>();
}
public Question(string description)
: this()
{
Check.Require(!string.IsNullOrEmpty(description) && description.Trim() != string.Empty);
Description = description;
}
#endregion
#region Properties
public virtual string Type { get; set; }
public virtual string Description { get; set; }
public virtual int Order { get; set; }
public virtual IList<Questionnaire> Questionnaires { get; set; }
public virtual IList<Alternative> Alternatives { get; set; }
public virtual Validator MyValidator { get; set; }
}
public class QuestionMap : ClassMap<Question>
{
public QuestionMap()
{
WithTable("ida_question");
Id(x => x.ID, "ida_question_id").WithUnsavedValue(0).GeneratedBy.UuidHex("");
Map(x => x.Description, "description").AsReadOnly();
Map(x => x.Type, "ida_question_type_id").AsReadOnly();
Component<Core.Validator>(x => x.MyValidator, m =>
{
m.Map(x => x.Type, "ida_question_type_id");
m.Map(x => x.RangeMin, "validate_min");
m.Map(x => x.RangeMax, "validate_max");
});
HasMany<QuestionnaireQuestion>(x => x.Questionnaires)
.Cascade.AllDeleteOrphan()
.WithKeyColumn("ida_question_id");
HasMany<Alternative>(x => x.Alternatives)
.IsInverse()
.WithKeyColumn("ida_question_id")
.AsBag().SetAttribute("cascade", "all");
}
}
QuestionnaireQuestion:
public class QuestionnaireQuestion : PersistentObjectWithTypedId<string>
{
protected QuestionnaireQuestion()
{
}
public virtual int QuestionOrder { get; set; }
public virtual Question Question {get;set;}
public virtual Questionnaire Questionnaire { get; set; }
}
public class QuestionnaireQuestionMap : ClassMap<QuestionnaireQuestion>
{
public QuestionnaireQuestionMap()
{
WithTable("ida_questionnaire_question");
SetAttribute("lazy", "false");
Id(x => x.ID, "ida_questionnaire_question_id")
.WithUnsavedValue(0)
.GeneratedBy.UuidHex("");
References(x => x.Question, "ida_question_id")
.WithForeignKey("ida_question_id")
.FetchType.Join();
References(x => x.Questionnaire, "ida_questionnaire_id")
.WithForeignKey("ida_questionnaire_id")
.FetchType.Join();
Map(x => x.QuestionOrder, "question_no");
}
}
Questionnaire:
public class Questionnaire : PersistentObjectWithTypedId<string>
{
#region Constructors
public Questionnaire()
{
Questions = new List<Question>();
}
public Questionnaire(string description) : this()
{
Check.Require(!string.IsNullOrEmpty(description) && description.Trim() != string.Empty);
Description = description;
}
#endregion
#region Properties
public virtual string Description { get; set; }
public virtual IList<Question> Questions { get; set; }
#endregion
}
public class QuestionnaireMap : ClassMap<Questionnaire>
{
public QuestionnaireMap(){
WithTable("ida_questionnaire");
SetAttribute("lazy", "false");
Id(x => x.ID, "ida_questionnaire_id")
.WithUnsavedValue(0)
.GeneratedBy.UuidHex("");
Map(x => x.Description);
HasMany<QuestionnaireQuestion>(x => x.Questions)
.Cascade.AllDeleteOrphan()
.WithKeyColumn("ida_questionnaire_id");
}
}
The result of running the SQL in the exception in the DB is 6 rows (the expected number) containing:
IDA4_2_: Guids
IDA1_2_: Guids
IDA1_5_1_: Guids
QUESTION2_5_1: Number (order
property)
IDA3_5_1_: Guids
IDA4_5_1: Guids
IDA1_3_0_: Guids
IDA2_3_0_: MOBILEPHONE (type
property)
DESCRIPT3_3_0_: mobiltelefon
(Description property)
VALIDATE4_3_0_: (RangeMin property)
VALIDATE5_3_0_: (RangeMax property)
The complete exception is:
[InvalidCastException: Cant use object type Questionnaires.Core.QuestionnaireQuestion as Questionnaires.Core.Question.]
NHibernate.Collection.Generic.PersistentGenericBag`1.ReadFrom(IDataReader reader, ICollectionPersister persister, ICollectionAliases descriptor, Object owner) +160
NHibernate.Loader.Loader.ReadCollectionElement(Object optionalOwner, Object optionalKey, ICollectionPersister persister, ICollectionAliases descriptor, IDataReader rs, ISessionImplementor session) +407
NHibernate.Loader.Loader.ReadCollectionElements(Object[] row, IDataReader resultSet, ISessionImplementor session) +412
NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) +472
NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +1010
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +114
NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +362
[GenericADOException: could not initialize a collection: [Questionnaires.Core.Questionnaire.Questions#CBDEDAFC183B4CD7AF1422423A91F589][SQL: SELECT questions0_.ida_questionnaire_id as ida4_2_, questions0_.ida_questionnaire_question_id as ida1_2_, questions0_.ida_questionnaire_question_id as ida1_5_1_, questions0_.question_no as question2_5_1_, questions0_.ida_question_id as ida3_5_1_, questions0_.ida_questionnaire_id as ida4_5_1_, question1_.ida_question_id as ida1_3_0_, question1_.ida_question_type_id as ida2_3_0_, question1_.description as descript3_3_0_, question1_.validate_max as validate4_3_0_, question1_.validate_min as validate5_3_0_ FROM ida_questionnaire_question questions0_ left outer join ida_question question1_ on questions0_.ida_question_id=question1_.ida_question_id WHERE questions0_.ida_questionnaire_id=?]]
NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +528
NHibernate.Loader.Collection.CollectionLoader.Initialize(Object id, ISessionImplementor session) +74
NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(Object key, ISessionImplementor session) +59
NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(InitializeCollectionEvent event) +573
NHibernate.Impl.SessionImpl.InitializeCollection(IPersistentCollection collection, Boolean writing) +150
NHibernate.Collection.AbstractPersistentCollection.ForceInitialization() +287
NHibernate.Engine.StatefulPersistenceContext.InitializeNonLazyCollections() +213
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +171
NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +493
NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId) +82
NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session) +54
NHibernate.Persister.Entity.AbstractEntityPersister.Load(Object id, Object optionalObject, LockMode lockMode, ISessionImplementor session) +206
NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +133
NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +948
NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +436
NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +236
NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) +1303
NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) +125
NHibernate.Impl.SessionImpl.Get(String entityName, Object id) +145
NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id) +66
NHibernate.Impl.SessionImpl.Get(Object id) +91
SharpArch.Data.NHibernate.RepositoryWithTypedId`2.Get(IdT id) +152
Questionnaires.Controllers.QuestionnaireController.Create(String username, String id) in C:\Documents and Settings\berbor\Mine dokumenter\Visual Studio 2008\Projects\Questionnaires\Questionnaires.Controllers\QuestionnaireController.cs:40
lambda_method(ExecutionScope , ControllerBase , Object[] ) +205
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +178
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +24
System.Web.Mvc.<>c__DisplayClassa.b__7() +52
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +254
System.Web.Mvc.<>c__DisplayClassc.b__9() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +192
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +350
System.Web.Mvc.Controller.ExecuteCore() +110
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +119
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +41
System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75
As for the reason I'm doing this:
One Questionnaire may have many Questions, One Question may have many Questionnaire.
I want to be able to give Questions some properties dependent on context (which Questionnaire is in), one question may be required in one questionnaire but not another etc. It's more than just order, that was just an example trying to keep it simple.
The exception is when trying to load data into Questionnaire.IList<Question>. And it's still thrown after I modify my IList<Questionnaire> to IList<QuestionnaireQuestion> and the respective mappings.
The problem then would be that Questionnaire.Questions is mapped as containing QuestionnaireQuestion but is of type Question. But if I try to map it as containing QuestionnaireQuestion my SQL does no join at all and it still fails.
Is this what I should do, just that I'm doing it wrong?
In the example I read he does it like I did as far as I can see, only difference is the newer version of FluentNhibernate that now uses Generics. So I need to specify the correct type.
Then, you should not have a List of Questionnaires in your Question class, but a List of QuestionnaireQuestion objects.
Are you sure you receive an ADO.NET Exception ?
Can you execute the generated query against your database ?
I don't understand your situation however, what is this QuestionairreQuestion class ? You're not using it in your Question class ?
I understand that you want to have an order in which your questions should occur.
So, if you have extra properties , I think you should use the QuestionairreQuestion class in your Question class.
But, if you only have this QuestionairreQuestion class in order to enforce an 'order' (and no other attributes), then, you could maybe use another Collection-Type instead of an IList.
There are collections in NHibernate which allow you to have ordered-collections (a map , for instance). So, you could use this collection in your Question class to hold Questionairre objects.
(ps: nhibernate generates an outer join, because you didn't specify in your mapping that the collection should not be null. So, an outer join is used because in some circumstances, it could be possible that you have a Question without Questionaires, as far as nhibernate is concerned).