Fluent NHibernate Decorating an Entity - c#

Using POCO and Fluent NHibernate, I would like to have the ability to take an object like User and be able to give it various properties. I would like these objects to be able to focus on their properties, not common properties. Currently, I have an AuditableEntity that I can derive from to give my entity the ability to have Audited properties like CreatedDateTime and ModifiedDateTime, but I would also like to have the ability to take out the need to implement my Id for every object, Id's should be able to be part of some other base object, so I could say this object has a Guid Id and this one has an int id and this one has no Id. Currently, my User object looks like this:
User.cs
namespace ZeroBase.Domain.Entities
{
public class User : AuditableEntity<User>
{
public virtual Guid Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string EmailAddress { get; set; }
public virtual IEnumerable<Comment> Comments { get; set; }
}
}
and my UserMap looks like this:
UserMap.cs
namespace ZeroBase.Infrastructure.Data
{
public class UserMap : AuditMap<User>
{
public UserMap()
{
Id(x => x.Id)
.Column("Id")
.GeneratedBy.Guid();
Map(x => x.Username);
Map(x => x.Password);
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.EmailAddress);
HasMany(x => x.Comments);
Table("Users");
}
}
}
Is there anyway to handle the Id creation in some sort of a generic base class and still have a separate class that objects can inherit from to become auditable?

Are you using AuditableEntity for classes without an id? Entity implies that the class has an identity. Based on this, I would say that you can add a Id property to AuditableEntity.
If you are using it for classes without an identity, like value objects, I would create another base class. Would something like this make sense?
class Entity {
Guid Id { get; set; }
public Entity() {
Id = Guid.NewGuid();
}
}
interface IAuditable<T> {
DateTime CreatedAt {get; set; }
// ...
}
class AuditableEntity<T> : Entity, IAuditable<T> {
DateTime CreatedAt { get; set; }
}
class AuditableValueObject<T> : IAuditable<T> {
DateTime CreatedAt { get; set; }
}
// Class that has both an identity and is auditable
class User : AuditableEntity<User> {
// ..
}
// Class without an identity but auditable
class Money : AuditableValueObject<Money> {
// ..
}
// Class with an identity but not auditable
class Customer : Entity {
// ..
}
Note: Entity, value objects etc. are terms of Domain Driven Design (DDD).

Related

Entity Framework - How to map a required property on one side with no navigation property on other side

I have the following two POCOs (Product and User) and a complex type (Tracking).
class Product() {
Guid Id { get; protected set; }
Tracking TrackingInfo { get; protected set; }
// Some other properties
}
class User() {
Guid Id { get; protected set; }
// Some other properties
}
class Tracking() {
User CreatedBy { get; protected set; }
Guid CreatedById { get; protected set; }
// Some other properties
}
The tracking class is a wrapper for tracking info and contains other tracking properties (date created, updated etc) and fulfils an interface for other purposes, but for now what I am concerned with is mapping the relationships between the Product TrackingInfo and the User.
Every Product must have an associated User which maps to the TrackingInfo.CreatedBy property.
The catch is that I don't want to create navigation properties on the User back to the Product - i.e. I don't want to have an ICollection<Product> ProductsCreated property.
I'm not sure how the relationship or the complex type mapping should be done in EntityFramework Code First. I have a mapping class as follows:
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
this.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("Id");
// Complex type mapping here?
}
}
How can I
Map the TrackingInfo complex type
Map the CreatedBy -> User single direction relationship?
Complex types cannot contain navigation properties. I recommend creating an abstract base class containing the Tracking properties as well as for its configuration:
public abstract class Tracking
{
public Guid CreatedById { get; set; }
public virtual User CreatedBy { get; set; }
}
public abstract class TrackingConfig<T> : EntityTypeConfiguration<T> where T: Tracking
{
public TrackingConfig()
{
HasRequired( t => t.CreatedBy )
.WithMany()
.HasForeignKey( t => t.CreatedById );
}
}
public class Product : Tracking
{
public Guid Id { get; set; }
}
public class ProductConfig : TrackingConfig<Product>
{
public ProductConfig()
{
}
}
...
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Configurations.Add( new ProductConfig() );
If you don't want the navigation properties, just don't define them:
public ProductMap()
{
this.HasRequired(p => p.Tracking)
.WithMany()
.HasForeignKey(p => p.TrackingId);
}
This way you will have access to the user of a product through Tracking, but you can't get the products from a user because there is no navigation property to Product defined in Tracking.

Why MapInheritedProperties() maps my entity into two tables?

I'm trying to map my entities following the TPC pattern.
I have an abstract base class Agent :
public abstract class Agent
{
public int Id { get; set; }
public DateTime? ChangeDate { get; set; }
public DateTime CreationDate { get; set; }
public string insuranceCompanyPolicyNumber { get; set; }
public int? KeySys { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
public string Status { get; set; }
public int? Transactionid { get; set; }
}
I have a child entity Suscriber :
public partial class Suscriber : Agent
{
public int? id_federateur { get; set; }
public string Number { get; set; }
public bool? suivi_client { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
}
When i try to configure the Suscriber like the following, i've noticed that the request generated by Entity Framework is wrong :
public class SuscriberConfiguration : EntityTypeConfiguration<Suscriber>
{
public SuscriberConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
m.Property(s => s.Number).HasColumnName("numero_souscripteur");
// Property(s => s.Number).HasColumnName("numero_souscripteur");
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
}
}
The request generated by Entity Framework is (The table Agent2 doesn't exist):
SELECT E'0X0X' AS "C1",
"Extent1"."id_personne",
"Extent1"."suivi_client",
"Extent1"."id_federateur",
"Extent2"."date_mod",
"Extent2"."date_cre",
"Extent2"."insuranceCompanyPolicyNumber",
"Extent2"."keySys",
"Extent2"."id_role",
"Extent2"."statut",
"Extent2"."vsc_transactionid",
"Extent2"."numero_souscripteur"
FROM "atlas"."Agent2" AS "Extent1"
INNER JOIN "atlas"."T_ACTEUR_SOUSCRIPTEUR" AS "Extent2" ON
"Extent1"."id_personne" = "Extent2"."id_personne"
I know that if i changed the configuration of suscriber like this, it will work
public class SuscriberConfiguration : EntityTypeConfiguration<Suscriber>
{
public SuscriberConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
// m.Property(s => s.Number).HasColumnName("numero_souscripteur");
Property(s => s.Number).HasColumnName("numero_souscripteur");
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
}
}
In other words, if i configure the property Number outside the scope of the method Map or i don't apply the method Property to the parameter of the lambda, it works. (Property(s => s.Number).HasColumnName("numero_souscripteur");)
Could somebody explain me how the method MapInheritedProperties() works?
Why Entity Framework generate a non existing table?
Thanks
This is because everything that you put in one mapping fragment...
Map(m => { ... })
...is mapped to the table that the fragment applies to. In fact it's saying: map all properties from Agent and property Number from Subsriber to table "T_ACTEUR_SOUSCRIPTEUR". EF silently assumes that the leftovers are mapped to another table it devices by naming conventions. This happens as soon as you map one property from the derived type. If you only use MapInheritedProperties(), all properties, including the derived ones, are mapped to the table in the mapping fragment.
I don't know if this is a bug or a feature. It's not very clear at least. Anyway, you should map other property names outside the mapping fragment, as you already found out. Of course it's it more clear if you do that in a separate statement:
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
Property(s => s.Number).HasColumnName("numero_souscripteur");
Or if you like:
Map(m => m.MapInheritedProperties());
ToTable("T_ACTEUR_SOUSCRIPTEUR");
Property(s => s.Number).HasColumnName("numero_souscripteur");

NHibernate: Load base class objects only

Any kind of help is welcome. Even if you can say (based upon your experience) that using an ORM for such a huge hierarchy is insane :).
Backgroud
My model layer has a pretty huge class hierarchy i.e. there are around 200 classes. The good/bad thing with hierarchy is that all of them have the same base class. The maximum distance between the base and leaf classes is 7 and the maximum number classes at any level in hierarchy is 80.
I am using nHibernate to save/load data from persistent storage.
Problem
The queries generated by nHibernate are pretty in efficient. e.g if I want to select ids of objects based upon some filter on a property in the base class, NHibernate will try to join all the tables in hierarchy/Union them depending which mapping strategy do I choose i.e. table per sub class or table per class hierarchy.
I understand that nHibernate does not which type of object until it can scan all the relevant tables. But what if I am only interested in the base class data at the moment. How to force nHibernate to load only the base class objects.
To illustrate my problem, here is a simplified version
public class Vehicle
{
public virtual Guid Identifier { get; set; }
public virtual int WheelsCount { get; set; }
public virtual Make Make { get; set; }
public virtual Model Model { get; set; }
}
public class Bike : Vehicle
{
public Bike()
{
WheelsCount = 2;
}
public virtual bool IsDirtBike { get; set; }
}
public class Car : Vehicle
{
public Car()
{
WheelsCount = 4;
}
public virtual bool IsFourWheelDrive { get; set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
}
public class Make
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Model> Models { get; set; }
}
public class Model
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Make Make { get; set; }
}
And the mappings are as follows
public class VehicleMap : ClassMap<Vehicle>
{
public VehicleMap()
{
Id(x => x.Identifier).GeneratedBy.Guid();
Map(x => x.WheelsCount);
References(x => x.Make).Column("MakeId");
References(x => x.Model).Column("ModelId");
Table("Vehicle");
Polymorphism.Explicit();
UseUnionSubclassForInheritanceMapping();
}
}
public class BikeMap : SubclassMap<Bike>
{
public BikeMap()
{
Map(x => x.IsDirtBike);
Table("Bike");
// Abstract();
}
}
public class CarMap : SubclassMap<Car>
{
public CarMap()
{
Map(x => x.Title);
Map(x => x.Description);
Map(x => x.IsFourWheelDrive);
Table("Car");
// Abstract();
}
}
public class MakeMap : ClassMap<Make>
{
public MakeMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Models)
.KeyColumn("MakeId");
Table("Make");
}
}
public class ModelMap : ClassMap<Model>
{
public ModelMap()
{
Id(x => x.Id);
Map(x => x.Name);
References(x => x.Make)
.Column("MakeId");
Table("Model");
}
}
Now if run the following query to load four wheeled vehicles, NHibernate will join vehicles, car and bike table. Whereas all I need right now is only the data stored in Vehicle table
List<Vehicle> vehicles = session.Query < Vehicle > ().Where(v => v.WheelsCount > 2).ToList();
Does anyone know how can I force nHibernate just load the data the is currently needed i.e. if it can return only vehicle objects instead of Car/Bike? With just a couple of tables in you schema you can overlook these queries by nHibernate but it really hurts when you have 200 tables :(.
P.S. In case there is a fault with model, please ignore that. This is not the real model. The actual model as stated earlier is much bigger. This model is there to illustrate the problem.
NHibernate has to join the tables to decide which type to return. otherwise polymorphism would be broken. Also it would be much harder to handle egde cases like abstract base class and so on.
Project only the properties you need and you are good to go
var vehicledatas = session.Query<Vehicle>()
.Where(v => v.WheelsCount > 2)
.Select(v => new { v.Id, v.WheelCount, v.Price })
.ToList();
If you need absolutly want only the base class then map it seperate for this use case
public class AlternateVehicleMap : VehicleMap
{
public AlternateVehicleMap()
{
EntityName("IAbsolutlyWantOnlyTheBaseClass");
Readonly(); // to make sure noone messes up
}
}
List<Vehicle> vehicles = session.CreateCriteria<Vehicle>("IAbsolutlyWantOnlyTheBaseClass").Add(Expression.Gt("WheelsCount", 2).List<Vehicle>();

fluent nhibernate - Many to Many mapping with attribute

I have 2 master tables which are linked by a map table as below
User [UserId,Name]
Resource [ResourceId,Name]
UserResourceMap [UserId,ResourceId,AccessLevel]
How would the Resource and User ClassMap with AccessLevel as a resource attribute look?
My Domain classes look like this
public class User
{
public virtual int UserId { get;protected set; }
public virtual string Name { get;set; }
}
public class Resource
{
public virtual int ResourceId { get;protected set; }
public virtual string Name { get;set; }
public virtual string AccessLevel { get;set; }//Issue-populate this using fluent
}
How can I use fluent to map the accessLevel attribute in the below code.
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("User");
Id(x => x.Key);
Map(x=>x.Name);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Table("Resource");
Id(x => x.Key);
Map(x=>x.Name);//Need some Map Here to make a hasManyToMany Map with attribute
}
}
Your domain model does not seem to match your database model - the Resource class has the property AccessLevel (i.e. one AccessLevel per Resource) but in the DB model AccessLevel is a column on the map table (i.e. one AccessLevel per User-Resource relation).
Assuming the DB model is the correct model one (fairly straightforward) way of mapping this would be to introduce a class like this.
public class UserResource {
public virtual int UserResourceId { get; protected set; }
public virtual User User { get; set; }
public virtual Resource { get; set; }
public virtual string AccessLevel { get; set; }
}
and map it in this way:
public class UserResourceMap : ClassMap<UserResource> {
public UserResourceMap() {
Table("UserResourceMap");
Id(x => x.UserResourceId);
References(x => x.User).UniqueKey("UniqueUserAndResource");
References(x => x.Resource).UniqueKey("UniqueUserAndResource");
Map(x => x.AccessLevel);
}
}
If you want bidirectional associations you could also add a Collection property on User and/or Resource and map these with HasMany(...).Inverse(). Of course, this kind of mapping would introduce a new UserResourceId column in the UserResourceMap table (using a composite key consisting of User and Resource would mitigate that).
Another solution would be to add an EntityMap association. If the association is owned by User it would be a Dictionary<Resource, string> property. Something like this might do the trick:
public class User {
public virtual int UserId { get; protected set; }
public virtual string Name { get; set; }
public virtual Dictionary<Resource, string> Resources { get; set; } // Resource -> AccessLevel
}
public class UserMap : ClassMap<User> {
public UserMap() {
Table("User");
Id(x => x.UserId);
Map(x => x.Name);
HasMany<Resource, string>(x => x.Resources).AsEntityMap().Element("AccessLevel");
}
}
As you've correctly identified in your database schema, this isn't a pure many-to-many relationship - it's two one-to-many relationships as the intermediate table has an attribute (the access level).
I therefore think your domain is missing an entity - there doesn't appear to be any relationship in your model between a user and the resources they can access.
How about something like this:
public class User
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
public virtual ICollection<UserResource> UserResources { get; set;}
}
public class UserResource
{
public virtual int Id { get; protected set; }
public virtual User User { get; set;}
public virtual Resource Resource { get; set;}
public virtual string AccessLevel { get; set;}
}
public class Resource
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
}
And mappings like:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.UserResource)
.AsSet()
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class UserResourceMap : ClassMap<UserResource>
{
public UserResourceMap()
{
Table("UserResourceMap");
Id(x => x.Id);
References(x => x.User).Not.Nullable();
References(x => x.Resource).Not.Nullable();
Map(x => x.AccessLevel);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Cache.ReadOnly();
Id(x => x.Id);
Map(x => x.Name);
}
}

NHibernate mapping to entity using common properties

I am using IQueryable<> to build up batching queries.
I have used views successfully to fetch information so the IQueryable<> can find it, but in this instance I can't work out how to map a view, as it depends on properties rather than the entity's ID.
Say I have this entity and mapping:
public class Calculation
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual TransactionTotal TransactionTotal { get; set; }
}
public class CalculationMap : ClassMap<Calculation>
{
public CalculationMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.Organisation).Not.Nullable().UniqueKey("OC");
References(x => x.Charge).Not.Nullable().UniqueKey("OC");
}
This is the class I need to get in there: I'm using a view to give me the total amount per Organisation and Charge:
public class TransactionTotal
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual decimal Amount { get; set; }
}
public class TransactionTotalMap : ClassMap<TransactionTotal>
{
public TransactionTotalMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Table("TransactionTotalsView");
References(x => x.Charge).Not.Nullable();
References(x => x.Organisation).Not.Nullable();
Map(x => x.Amount).Precision(15).Scale(2).Not.Nullable();
}
}
Other places I have used views I have successfully used mappings like HasOne(x => x.TransactionTotal); but in this instance I need to tell Nhibernate to use the Organisation and Charge properties as the key.
Is this even possible? If so, how do I map the TransactionTotal class to the Calculation class?
Edit: I have used CompositeId in TransactionTotalMap as suggested by #David:
CompositeId().KeyProperty(x => x.Organisation.Id).KeyProperty(x => x.Charge.Id);
I'm still stuck on what to put in the CalculationMap though.
use the CompositeId() method in your mapping

Categories