Fluent NHibernate mappings for localization - nhibernate

I am trying to build a Database from NHibernate mappings and have run into a problem.
I have many classes with localized string values:
public class MyClass1 {
public virtual int Id { get; set; }
public virtual ShortString Name { get; set; }
public virtual LongString Description { get; set; }
}
public class MyClass2 {
public virtual int Id { get; set; }
public virtual ShortString Name { get; set; }
public virtual LongString Description { get; set; }
}
and Languages like
public class Language {
public virtual string Code { get; set }
public virtual string Name { get; set }
}
My ShortString and LongString classes both look the same:
public class ShortString {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Values { get; set; }
}
What I want to achieve are two tables (ShortString and LongString) looking like this:
TABLE ShortString
-----------------
Id (int)
LanguageCode (nvarchar(8))
Value (nvarchar(256)) (or ntext for the LongString Table)
...with Id AND LanguageCode as primary keys and a ForeignKey to the Language Table.
And in the MyClass1 and MyClass2 tables, I want to have NameId (int) and DescriptionId (int) columns mapped to ShortString and LongString tables respectively.
I am totally stuck. How can I achieve this?

Maybe you could ditch short and long string altogether
public class MyClass1 {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Name { get; set; }
public virtual IDictionary<Language, string> Description { get; set; }
}
public class MyClass2 {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Name { get; set; }
public virtual IDictionary<Language, string> Description { get; set; }
}
and use the folling Mapping
public class MyClass1Map : ClassMap<MyClass1>
{
public MyClass1Map()
{
[...]
HasMany(mc => mc.Name)
.Table("ShortString")
.KeyColumn("id")
.AsEntityMap("language_id")
.Element("value")
HasMany(mc => mc.Description)
.Table("LongString")
.KeyColumn("id")
.AsEntityMap("language_id")
.Element("value", e => e.Length(1000))
}
}
I cant test it right now so there might be tweaking nessesary

Related

Mapping multiple properties of a same type with HasMany via automapping

I am trying to map properties of the same type on a OneToMany association. I tried to distinguish with Description but kinda stuck here.
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
[Description("From")]
public virtual IList<Message> FromMessageList { get; set; }
[Description("To")]
public virtual IList<Message> ToMessageList { get; set; }
}
public class Message
{
public virtual int MessageId { get; set; }
public virtual string Text { get; set; }
[Description("From")]
public virtual User FromUser { get; set; }
[Description("To")]
public virtual User ToUser { get; set; }
}
public class DefaultHasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.OtherSide.Property.GetDescription() == instance.Member.GetDescription())
{
if (instance.Member.GetDescription() != null)
instance.Key.Column(instance.Member.GetDescription() + "Id");
else
instance.Key.Column(instance.OtherSide.Property.Name + "Id");
instance.Fetch.Select();
}
}
}
public class DefaultReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
if (instance.Property.GetDescription() != null)
instance.Column(instance.Property.GetDescription() + "Id");
else
instance.Column(instance.Property.Name + "Id");
instance.Fetch.Select();
}
}
For one to many relationships I generally use coding like :
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
[Description("From")]
public virtual ICollection<Message> FromMessageList { get; set; }
[Description("To")]
public virtual ICollection<Message> ToMessageList { get; set; }
}
public class Message
{
public int MessageId { get; set; }
public string Text { get; set; }
[Description("From")]
public virtual User FromUser { get; set; }
// From user foreign key column
[ForeignKey("FromUser")]
public int FromUserId {get;set;}
[Description("To")]
public virtual User ToUser { get; set; }
// ToUser foreign key column
[ForeignKey("ToUser")]
public int ToUserId {get;set;}
}
Try to use ICollection instead of IList - this solved many issues for me.
Add foreign key column names; it makes mapping simpler and filtering in queries easier.

Fluent NHibernate mapping throws an exception of 'Id is not mapped'

I need help with auto mapping in Fluent Nhibernate. Here's the tables I want to have in my app (they are many of them, but I want to start from mapping only a few of them)
Well, I'd like to use the AutoMapping functionality because I don't want to write the mapping classes for more than 100 tables...
Anyway, here's the error thrown when creating the SessionFactory (the code is at the end of this post)
The entity 'FilterConfig' doesn't have an Id mapped.
Use the Id method to map your identity property. For example: Id(x => x.Id).
Entities (I hope I created them correctly):
public partial class UserLogin
{
public UserLogin()
{
this.UserMessages = new List<UserMessage>();
this.UserMessagesReceivers = new List<UserMessagesReceiver>();
}
public virtual int ID { get; set; }
public virtual int UserTypeID { get; set; }
public virtual int? StudentID { get; set; }
public virtual int? HeadmasterID { get; set; }
public virtual int? ParentID { get; set; }
public virtual string UniqueID { get; set; }
public virtual bool ShowMyPhoneNumber { get; set; }
public virtual bool IsBanned { get; set; }
public virtual string Login { get; set; }
public virtual string Password { get; set; }
public virtual bool WasPasswordSent { get; set; }
public virtual string Email { get; set; }
public virtual string UserPicture { get; set; }
public virtual IList<UserMessage> UserMessages { get; set; }
public virtual IList<UserMessagesReceiver> UserMessagesReceivers { get; set; }
}
public partial class UserMessage
{
public UserMessage()
{
this.UserMessagesReceivers = new List<UserMessagesReceiver>();
this.UserMessagesReplies = new List<UserMessagesReply>();
}
public virtual int ID { get; set; }
public virtual DateTime Date { get; set; }
public virtual DateTime? LastCheckDate { get; set; }
public virtual int UserLoginID { get; set; }
public virtual string Description { get; set; }
public virtual bool HasNonCheckedReplies { get; set; }
public virtual UserLogin UserLogin { get; set; }
public virtual IList<UserMessagesReceiver> UserMessagesReceivers { get; set; }
public virtual IList<UserMessagesReply> UserMessagesReplies { get; set; }
}
public partial class UserMessagesReceiver
{
public UserMessagesReceiver()
{
this.WasMessageChecked = false;
this.UserMessagesReplies = new List<UserMessagesReply>();
}
public virtual int ID { get; set; }
public virtual int UserMessagesID { get; set; }
public virtual int ReceiverLoginID { get; set; }
public virtual bool WasMessageChecked { get; set; }
public virtual DateTime? LastCheckedDate { get; set; }
public virtual UserLogin UserLogin { get; set; }
public virtual UserMessage UserMessage { get; set; }
public virtual IList<UserMessagesReply> UserMessagesReplies { get; set; }
}
public partial class UserMessagesReply
{
public UserMessagesReply()
{
}
public virtual int ID { get; set; }
public virtual DateTime Date { get; set; }
public virtual int UserMessagesID { get; set; }
public virtual int? UserMessagesReceiverID { get; set; }
public virtual string Description { get; set; }
public virtual UserMessage UserMessage { get; set; }
public virtual UserMessagesReceiver UserMessagesReceiver { get; set; }
}
Configuration:
public class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool IsId(Member member)
{
return member.Name == member.DeclaringType.Name + "ID";
}
}
private static AutoPersistenceModel CreateAutomappings()
{
return AutoMap.AssemblyOf<AutomappingConfiguration>(new AutomappingConfiguration());
}
private static ISessionFactory CreateSessionFactory()
{
var cfg = new AutomappingConfiguration();
return Fluently.Configure()
.Database(MySQLConfiguration.Standard
.ConnectionString("..."))
.Mappings(m => m.AutoMappings
.Add(AutoMap.AssemblyOf<UserLogin>(cfg))
.Add(AutoMap.AssemblyOf<UserMessage>(cfg))
.Add(AutoMap.AssemblyOf<UserMessagesReceiver>(cfg))
.Add(AutoMap.AssemblyOf<UserMessagesReply>(cfg)))
.BuildSessionFactory();
}
in the configuration you said that the Ids are named like UserLoginID but in the class they are defined as public virtual int ID { get; set; } so change
return member.Name == member.DeclaringType.Name + "ID";
to
return member.Name == "ID";
Some additional info:
CreateAutomappings() seems to be not used
AutomappingConfiguration should at least override ShouldMap(Member) to filter on the namespace (e.g. member.Namespace.StartsWith(typeof(UserMessage).Namespace)) otherwise sooner or later utility classes and the like will be mapped as well
AutoMap.AssemblyOf<> should be called per assembly containing types not per type

How to map a derived class using an EntityBase class on FluentNHibernate

I have an EntityBase class for FluentNHibernate:
public abstract class EntityBase<T>
{
public EntityBase()
{
}
public static T GetById(int id)
{
return (T)Hibernate.Session.Get<T>(id);
}
public virtual void Save()
{
using (var transaction = Hibernate.Session.BeginTransaction())
{
Hibernate.Session.SaveOrUpdate(this);
transaction.Commit();
}
}
public static IList<T> List()
{
return Hibernate.Session.CreateCriteria(typeof(T)).List<T>();
}
public static IList<T> ListTop(int i)
{
return Hibernate.Session.CreateCriteria(typeof(T)).SetMaxResults(i).List<T>();
}
public virtual void Delete()
{
using (var transaction = Hibernate.Session.BeginTransaction())
{
Hibernate.Session.Delete(this);
transaction.Commit();
}
}
}
I have a base member class also a table in database:
abstract public class BaseMember:EntityBase<BaseMember>
{
public virtual int Id { get; set; }
public virtual string Email { get; set; }
public virtual string Password { get; set; }
public virtual string RecordDate { get; protected set; }
public BaseMember()
{
}
}
I have another Member class that is deriving from BaseMember class:
public class IndividualMember : BaseMember
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual string MobilePhoneNumber { get; set; }
public virtual DateTime BirthDate { get; set; }
public virtual bool Gender { get; set; }
public virtual string ProfileImage { get; set; }
public virtual string AddressDefinition { get; set; }
public virtual string ZipCode { get; set; }
public virtual DateTime RecordDate { get; set; }
public IndividualMember()
{
}
}
How can I map those classes with BaseMember and IndividualMember tables in db?
There are different types of Inheritance mapping strategies in Fluent NHibernate.
You can use SubclassMap mapping for derived class.
Strategies : Table-per-class-hierarchy, Table-per-subclass and Table Per Concrete Class.
For table-per-class-hierarchy strategy, you just need to specify the discriminator column.
For more reference :
http://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
https://github.com/jagregory/fluent-nhibernate/wiki/Fluent-mapping#wiki-subclasses

Nhibernate and entity base class

I am trying to implement abstract base entity class which has overriden equals and GetHashcode...Here is my entity base class
public abstract class Entity<TId>
{
public virtual TId Id { get; protected set; }
protected virtual int Version { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as Entity<TId>);
}
private static bool IsTransient(Entity<TId> obj)
{
return obj != null &&
Equals(obj.Id, default(TId));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual bool Equals(Entity<TId> other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
public override int GetHashCode()
{
if (Equals(Id, default(TId)))
return base.GetHashCode();
return Id.GetHashCode();
}
}
How does the value of entity base Id gets assigned?
The primary keys of my classes have different datatypes and also names are different for each class.
Here is the sample of my classes:
public class Product : Entity
{
public virtual Guid ProductId { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Decimal UnitPrice { get; set; }
}
public class Customer : Entity
{
public virtual int CustomerID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual int Age { get; set; }
}
I am bit confused on how to set ID property of the base class. Can anyone please suggest me on this, I will appreciate any help.
You just need to pass a type to the inherited base class.
See comments in the entities:
public class Product : Entity<Guid>
{
// The ProductId property is no longer needed as the
// Id property on the base class will be of type Guid
// and can serve as the Id
//public virtual Guid ProductId { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Decimal UnitPrice { get; set; }
}
public class Customer : Entity<int>
{
// The CustomerID property is no longer needed as the
// Id property on the base class will be of type int
// and can serve as the Id
// public virtual int CustomerID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual int Age { get; set; }
}
Now in your NHibernate mapping files, just specify what the database column is for your Id properties.

How to best design the entity data classes for the following SQL Schema?

I have the following database schema:
http://lh4.ggpht.com/_SDci0Pf3tzU/SdM3XnAmmxI/AAAAAAAAEps/Ie3xW3ZVNfQ/styleerror.png
The issue is how to create the entity data class in Nhibernate?
Is this better:
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
}
public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; private set; }
}
Or is this better?
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
}
public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
}
public class StoreProduct
{
public virtual List<Product> Products{get;set;}
public virtual List<Store> Stores{get;set;};
}
I think the first one is easier to understand than the second, isn't it?
If you modify the 'StoreProduct' table, so that it has no surrogate primary key, but a primary key which exists of the 2 foreign key columns (ProductId & StoreId), then you can simply limit yourself to 3 entities:
- Employee
- Product
- Store
Your Store class could then have a Set of Products, which can simply be mapped as a many-to-many relationship.
public class Store
{
public int Id {get;set;}
public string Name {get;set;}
public ISet<Product> Products = new HashedSet<Product>();
}
And in the Store.hbm.xml mapping:
<set name="Products" table="StoreProducts">
<key column="Store_Id" />
<many-to-many class="Product" column="Product_Id" />
</set>
So, to answer your question: first option is better.