I have two similar tables (Table1, Table2) so I created a base abstract class which has common properties. Each table has a column indicating status of record processing. I'd like to map this columns to one enum:
enum RecordStatus
{
UnkownStatus,
NotProcessed,
Processed,
}
Unfortunately for each table I need mapping different values for enums.
So I created two converters (Table1StatusConverter, Table2StatusConverter) which inherit from EnumType<RecordStatus> and setup in mappings. It works partially. Partially because NHibernate use only one converter in both classes.
Is this bug or maybe it works like described by design? Is there any workaround for this?
Edit: I write code from memory because the moment I do not have access to it
Entities:
class abstract TableBase
{
public Guid Id { get; protected set; }
public string Sender { get; protected set; }
public DateTime ReceiveTime { get; protected set; }
public RecordStatus Status { get; set; }
}
class Table1 : TableBase
{
public string Message { get; set; }
}
class Table2 : TableBase
{
public ICollection Parts { get; protected set; }
}
Converters: Table1StatusConverter and Table2StatusConverter override the same method, but in different ways.
class Table1StatusConverter : EnumType<RecordStatus>
{
public override object GetValue(object enumValue) { ... }
public override object GetInstance(object value) { ... }
}
Mappings:
Table1.hbm.xml
<class name="Table1" table="Table1">
..
<property name="Status" type="MyAssembly.Table1StatusConverter, MyAssembly" />
..
</class>
Table2.hbm.xml
<class name="Table2" table="Table2">
..
<property name="Status" type="MyAssembly.Table2StatusConverter, MyAssembly" />
..
</class>
This doesn't sound like a good use of inheritance. However, you could accomplish this by mapping the integer value for the enums as a protected field in the base class and use public properties in the extended classes to cast to and from the appropriate enum.
May be you need to override this properties explicitly?
class abstract TableBase
{
// ...
public virtual RecordStatus Status { get; set; }
}
class Table1 : TableBase
{
public string Message { get; set; }
public override RecordStatus Status { get; set; }
}
class Table2 : TableBase
{
public ICollection Parts { get; protected set; }
public override RecordStatus Status { get; set; }
}
Related
I would like to map the following hierarchy of entities to the TestViewModel class. I have TestViewModel class with the same member names and perhaps I will add more members to the view model. I am using AutoMapper.
public class TestProfile : Profile
{
public TestProfile ()
{
CreateMap ??????
}
}
public class Test
{
public List<Test1> Tests1 { get; set; }
public int TestId { get; set; }
}
public class Test1
{
public int Test1Id { get; set; }
public string Name { get; set; }
public List<Document> Documents { get; set; }
}
public class Document
{
public int DocumentId { get; set; }
public DateTimeOffset? ChangeDate { get; set; }
public List<Payload> Payloads { get; set; }
}
public class Payload
{
public string PayloadName { get; set; }
}
You didn't tell us what your TestViewModel class looks like, and whether you also have DocumentViewModel, PayloadViewModel, etc. Typically if you are mapping to a another set of classes that have the same naming and structure, like a set of ViewModels, you will want to have a configuration like this:
public class TestProfile : Profile
{
public TestProfile()
{
CreateMap<Test, TestViewModel>();
CreateMap<Test1, Test1ViewModel>();
CreateMap<Document, DocumentViewModel>();
CreateMap<Payload, PayloadViewModel>();
}
}
This will map all like-named properties between the two sets of classes. If your TestViewModel shares the same child entities, then you only need the first line.
On the service side I have an abstract base class like so:
[DataContract]
public abstract class EntityBase : IObjectState, IDatabaseMetaData
{
[NotMapped]
[DataMember]
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
[DataMember] public DateTime InsertDatetime { get; set; }
[DataMember] public int InsertSystemUserId { get; set; }
[DataMember] public DateTime? UpdateDatetime { get; set; }
[DataMember] public int? UpdateSystemUserId { get; set; }
public virtual SystemUser InsertSystemUser { get; set; }
public virtual SystemUser UpdateSystemUser { get; set; }
#endregion
}
Here is an implementing class (data contract):
[DataContract(Namespace = Constants.MyNamespace)]
public class AccountClass : EntityBase
{
[DataMember] public int AccountClassId { get; set; }
[DataMember] public string AccountClassCode { get; set; }
[DataMember] public string AccountClassDesc { get; set; }
}
On the client side I have essentially duplicated contracts. Here is the Client.AccountClass:
public class AccountClass : ObjectBase
{
private int _accountClassId;
private string _accountClassCode;
private string _accountClassDesc;
public int AccountClassId
{
get { return _accountClassId;}
set
{
if (_accountClassId == value) return;
_accountClassId = value;
OnPropertyChanged(() => AccountClassId);
}
}
public string AccountClassCode
{
get { return _accountClassCode; }
set
{
if (_accountClassCode == value) return;
_accountClassCode = value;
OnPropertyChanged(() => AccountClassCode);
}
}
public string AccountClassDesc
{
get { return _accountClassDesc; }
set
{
if (_accountClassDesc == value) return;
_accountClassDesc = value;
OnPropertyChanged(() => AccountClassDesc);
}
}
}
..and here is the parts of ObjectBase that matter:
public abstract class ObjectBase : IObjectState, IDatabaseMetaData
{
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
public DateTime InsertDatetime { get; set; }
public int InsertSystemUserId { get; set; }
public DateTime? UpdateDatetime { get; set; }
public int? UpdateSystemUserId { get; set; }
#endregion
}
When I debug the service in my WcfMessageInspector.BeforeSendReply, I can see the message correctly sending the IObjectState and IDatabaseMetaData values. However, on the client side, they are always null (or default values). I have tried using KnownTypes, applying the namespace to the abstract class. The only way I can serialize everything correctly is to get rid of the interfaces and base classes all together and put the properties directly on the Client/Server AccountClass object. What am I missing here? Thanks.
Update 1
This seems to be a namespace thing. If I move my EntityBase and ObjectBase into the same CLR Namespace, everything works (with no KnownType attributes). In my client contract's AssemblyInfo.cs file I have this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Project.Name.Client.Entities")]
I tried adding ContractNamespaces here to no avail. Like I said, unless the EntityBase and ObjectBase are in the same namespace, it won't work. However, this is a problem for me because it creates a circular reference, unless I move a lot of stuff around.
Any idea how I can see what the full data contract (namespaces, DataMembers, etc) looks like just before/after serialization on the client/server? I tried intercepting the OnSerializing event without much luck. Thanks again.
This was a namespace issue.
I explicitly add the correct namespace to all parties involved and everything works great. One thing I notice is that the ContractNamespace's ClrNamespace in your AssemblyInfo.cs file should match the AssemblyTitle. Also, putting more than one ContractNamespace in the AssemblyInfo.cs does nothing. For example, I was doing this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities")]
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities.Core")]
Any POCO in the Company.Project.Client.Entities.Core would not serialize correctly until I explicitly put the DataContract namespace on it like so
[DataContract(Namespace = Constants.MyNamespace)]
public class SomeObject
{
[DataMember] public string SomeProperty { get; set; }
//..etc
}
Alternatively, I could have restructured the project so SomeObject was in the Company.Project.Client.Entities namespace and that would have worked.
Finally, the most helpful thing to debugging this was looking at the WSDL, and then using a custom IDispatchMessageInspector to see the actual messages AfterReceiveRequest and BeforeSendReply. Hopefully this helps someone.
My class:
[PersistClass]
public class ExternalAccount
{
public virtual AccountType AccountType { get; set; }
public virtual int Id { get; private set; }
public virtual User User { get; set; }
public virtual Dictionary<string, string> Parameters { get; set; }
public ExternalAccount()
{
Parameters = new Dictionary<string, string>();
}
}
The Dictionary is not getting mapped. I understand that automapping doesn't work by default with Dictionaries, how do I configure the mapping? All Parameters is is a list of key/value pairs - so I would expect them to be stored in a table with a foreign key to the externalaccount table. I know I can do this with another class - but it makes access to the parameters in the class more difficult - I'd rather have to configure the complexity once.
Please bear in mind I am new Fluent and to nHibernate.
Thanks
Using a simple class relationship such as the following:
public class Foo {
public virtual IDictionary<string, Bar> Bars { get; set; }
}
public class Bar {
public virtual string Type { get; set; }
public virtual int Value { get; set; }
}
You can map this with Fluent NHibernate in this way:
mapping.HasMany(x => x.Bars)
.AsMap(x => x.Type);
Where Bar.Type is used as the index field into the dictionary.
FluentNHibernate mapping for Dictionary
I have the following situation with fluent nhibernate:
public class Stuff
{
public Stuff()
{
Entities = new List<Entity>();
}
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
}
public abstract class Entity
{
public virtual int Id { get; set; }
public virtual string Type { get; set; }
public virtual Stuff Stuff { get; set; }
}
public class Person : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class Animal : Entity
{
public virtual string Species { get; set; }
}
And then, i have the following code to use automap and generate these mappings:
var sessionFactory =
Fluently.Configure().Database(persistenceConfigurer).Mappings(
m =>
m.AutoMappings.Add(
AutoMap.Source(new Types(typeof(Entity), typeof(Person), typeof(Animal), typeof(Stuff))))
.ExportTo(#"e:\")).ExposeConfiguration(BuildSchema).BuildSessionFactory();
however, what's happening is that i get the following exception:
---> NHibernate.MappingException: Association references unmapped class: ConsoleApplication1.Models.Entity
if i make the entity class non abstract this works, however, i'd like to avoid having that table in the database but still maintain the hierarchy concept with the re
You need to add your auto mappings like this
AutoMap.AssemblyOf<Entity>(yourConfiguration).IgnoreBase<Entity>();
Not only will this ignore your Entity base class but you don't need to add each entity manually so long as each model inherits from Entity.
Recently I came across a strange behavior in Automapping of Fluent NHibernate. I have the following class structure (some properties cut off for the sake of brewity).
public class UserGroup
{
public virtual UserGroup ParentGroup { get; set; }
public virtual UserGroupMember Manager { get; protected set; }
public virtual ISet<UserGroupMember> Members { get; protected set; }
}
and
public class UserGroupMember : BaseEntity
{
public virtual User User { get; set; }
public virtual UserGroup Group { get; set; }
}
The mapping for UserGroup:
public class UserGroupMap : IAutoMappingOverride<UserGroup>
{
public void Override(AutoMapping<UserGroup> mapping)
{
mapping.HasMany(el => el.Members)
.Cascade
.AllDeleteOrphan().Inverse().LazyLoad();
}
}
The automapping creates two column (both of which are foreign keys) in the UserGroupMember table to reflect the relation between UserGroup and UserGroupMembers. I've found out that the generated mapping contains wrong column (as seen below):
<set cascade="all-delete-orphan" inverse="true" lazy="true" name="Members" mutable="true">
<key>
<column name="Parent_Id" />
</key>
<one-to-many class="Groups.Data.UserGroupMember, Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>
which results in wrong queries:
While insert in UserGroupMember - Group_Id is used (which is right), not using Parent_Id
While select in UserGroupMember - Parent_Id is used
Group_Id is the column in UserGroupMember mapping file which reflects the Group property in UserGroupMember.
I tried to modify the mapping adding .KeyColumn("Group_Id") and it is solves the problem. But is there any way to make Fluent NHibernate 'think the right way'?
This is from memory, as I don't have test code ready.
When using bidirectional many-to-many, you sometimes have to help FHN figure columns names, if they're not "alike" on both sides.
For example this should map correcly
public class User
{
public IList<Group> Groups { get; set; }
}
public class Group
{
public IList<User> Users { get; set; }
}
While this would not
public class User
{
public IList<Group> BelongsTo { get; set; }
}
public class Group
{
public IList<User> Contains { get; set; }
}
As a rule of thumb, if automapping (with or without conventions) doesn't generate right columns names, especially for non trivial cases, do not hesitate to put an override to set those column names manually.