I'm using nhibernate envers to audit my data / save previous versions.
What I'd like to do is store previous versions against the parent entity.
Something like this:
public abstract class BookBase
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual string Author { get; set; }
}
public class Book : BookBase
{
public virtual ICollection<BookRevision> PreviousVersions { get; set; }
}
public class BookRevision : BookBase
{
public virtual int VersionNumber { get; set; }
public virtual DateTime VersionTimeStamp { get; set; }
}
Is that possible with envers nhibernate (using fluent nHibernate for mappings)
What would my envers config need to look like?
What would my mappings need to look like?
Envers handles auditing for you, you don't have to define your own auditing types in your domain model.
Define (and map it as normal) your entity
public class Book
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual string Author { get; set; }
}
If you want to do auditing on Book modifications, configure Envers like this
var enversCfg = new FluentConfiguration();
enversCfg.Audit<Book>();
yourNhCoreConfiguration.IntegrateWithEnvers(enversCfg);
Related
I have a problem with NHibernate for a longtime which I solved by non-optimal ways/workarounds.
First of all, I'm using WCF REST to communicate with my client application. As you know, serializing persisted entities is not a best practise and always causes other problems. Thus, I always map my entities to DTO's with NHibernates Transformers. The problem is that I have entities which are more complex to use Transformers to convert them.
How can I map sub entities to sub dto's by using transformers or any other nhibernate feature?
Note: I don't want to use 3rd parties like Automapper.
These are the Entities and DTO's which I want to map. Variable names are exactly same with each other.
Entity Classes:
EntityType
public class crmEntityType : EntityModel<crmEntityType>
{
public crmEntityType()
{
Association = new List<crmEntityType>();
Fields = new List<crmCustomField>();
}
public virtual int ID { get; set; }
public virtual string Title { get; set; }
public virtual ICollection<crmEntityType> Associations { get; set; }
public virtual ICollection<crmCustomField> Fields { get; set; }
}
CustomFields
public class crmCustomField : EntityModel<crmCustomField>
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual crmEntityType EntityType { get; set; }
}
DTO's
EntityTypeDTO
[DataContract]
public class EntityTypeDTO
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public IList<CustomFieldDTO> Fields { get; set; }
[DataMember]
public int[] Associations { get; set; }
}
CustomFieldDTO
[DataContract]
public class CustomFieldDTO
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int EntityType { get; set; }
[DataMember]
public int FieldType { get; set; }
}
I found my solution by spending my day and night to work it out. Finally, I've got the best solution I could find. I hope it helps someone in my condition some day.
This linq query works with just one database round-trip. I think it maps the classes in memory.
return (from entityType in Provider.GetSession().Query<crmEntityType>()
.Fetch(x => x.Association)
.Fetch(x => x.Fields)
.AsEnumerable()
select new EntityTypeDTO()
{
ID = entityType.ID,
Title = entityType.Title,
Association = entityType.Association.Distinct()
.Select(asc => asc.ID).ToArray<int>(),
Fields = entityType.Fields.Distinct()
.Select(fi => new CustomFieldDTO
{ ID = fi.ID,
Name = fi.Name,
Value = fi.Value,
EntityType = fi.EntityType.ID,
Type = fi.Type
}).ToList()
}).ToList();
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.
I searched a lot about how to map multiple inheritance or multiple interface implantation using EntityFramework or NHibernate But I didn't find anything useful.
I Simply want to map this structure using NHibernate:
public interface IA
{
string A { get; set; }
}
public interface IB
{
string B { get; set; }
}
public class C : IA, IB
{
string A { get; set; }
string B { get; set; }
}
As far as i know mapping this structure to a relational database means just to have foreign keys related with the interfaces primary keys, therefore the interfaces should have Keys like these:
public interface IA
{
Guid AId { get; set; }
string A { get; set; }
}
public interface IB
{
Guid BId { get; set; }
string B { get; set; }
}
public class C : IA, IB
{
public virtual Guid AId { get; set; }
public virtual Guid BId { get; set; }
public virtual string A { get; set; }
public virtual string B { get; set; }
}
But how to map this structure using NHibernate Or EntityFramework,and I don't know why multiple interface mapping is not mentioned in their documentation!
In NHibernate, you'll just map C as if the interfaces didn't exist.
You'll still be able to query on the interfaces, thanks to implicit polymorphism.
You will map it as any other class because this is not inheritance mapping. Moreover your code cannot be compiled because you must implement all properties in class C so you will get:
public interface IA
{
Guid AId { get; set; }
string A { get; set; }
}
public interface IB
{
Guid BId { get; set; }
string A { get; set; }
}
public class C : IA, IB
{
public virtual Guid AId { get; set; }
public virtual Guid BId { get; set; }
public virtual string A { get; set; }
}
Now your code can be compiled and you have class as any other. You will map AId and BId as composite key (depending on used ORM) and you are done. This is not inheritance because you have just single entity and no base enity. At least this is how it works with Entity framework.
As what I've founded it's not possible to have multiple inheritance in a relational database due to the concept and what Diego said is true in a "not interfaces get persisted scenario".
I have a composite object set up Project->Appraisal, My appraisal object has a ApprovedMentor object which is not required but when i go to save project Nhib throws and error to say that ApprovedUser has not been set. but its not set because its not a required field. How do i set up this using fluent auto mapping, is it possible?
public class MentoringProject : BaseEntity
{
public MentoringProject()
{
Appraisal = new Appraisal();
}
[NotNullNotEmpty]
[Length(Min=25, Max=1000)]
public virtual string Description { get; set; }
[Length(Min=25, Max=1000)]
public virtual string SupportRequired { get; set; }
[NotNullNotEmpty]
public virtual System.DateTime? DateSubmitted { get; set; }
[NotNullNotEmpty]
public virtual System.DateTime? ClosingDate { get; set; }
[NotNullNotEmpty]
[Size(Min=1)]
public virtual short Duration { get; set; }
[NotNullNotEmpty]
public virtual string Skills { get; set; }
public virtual Appraisal Appraisal { get; set; }
}
public class Appraisal : BaseEntity
{
public Appraisal()
{
ShortlistedMentors = new List<User>();
ApprovedMentor = new User();
College = new RefData();
}
#region Primitive Properties
public virtual bool Decision { get; set; }
public virtual System.DateTime? ApprovedDate { get; set; }
public virtual System.DateTime? AcceptedDate { get; set; }
public virtual System.DateTime? CompletionTargetDate { get; set; }
public virtual string RejectionReason { get; set; }
#endregion
#region Navigation Properties
public virtual IList<User> ShortlistedMentors { get; set; }
public virtual User ApprovedMentor { get; set; }
public virtual RefData College { get; set; }
#endregion
}
It looks to me that you just want to ignore the ShortlistedMentors property which you need to do in your mapping class like this:
map.IgnoreProperty(p => p.ShortlistedMentors);
This answer was posted in this question.
I think i have solved this, when binding the UI to the controller in MVC, MVC creates an empty User object and because that object has required fields set on it using nhib validator and nhib was trying to create a new user object, I got round this by checking if there is a user realtionship to add, if not I set the Appraisal.ApprovedMentor==null