Question

I am implementing a relatively simple model of user management using Castle Active Record with NHibernate on top of MySql, and I have ran into an issue.

Let us say, I have two tables _users and _passwords described by the following SQL create statements

CREATE TABLE _users (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  username char(32) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY username_UQ (username),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE _passwords (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  creation_date datetime NOT NULL,
  user_id bigint(20) NOT NULL,
  password_hash char(64) NOT NULL,
  valid_end_date datetime NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY user_id_password_UQ (user_id,password_hash),
  KEY user_passwords_FK (user_id),
  CONSTRAINT user_passwords_FK FOREIGN KEY (user_id) REFERENCES _users (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

The idea is to keep a primitive password history, therefore, the passwords are kept in a separate table _passwords, which has many-to-one relation with the _users table.

Now, the following C# code models this structure using Castle Active Records

namespace DataEntities
{
    [ActiveRecord("_users")]
    public class User : ActiveRecordBase<User>
    {
        [PrimaryKey(PrimaryKeyType.Identity, "id", Access = PropertyAccess.NosetterLowercase)]
        public ulong Id
        {
            get;
            set;
        } // Id

        [Property("username", ColumnType = "String", NotNull = true, Unique = true)]
        [ValidateIsUnique]
        public string Username
        {
            get;
            set;
        } // Username

        [HasMany(typeof(Password))]
        public IList<Password> Passwords
        {
            get;
            set;
        } // Passwords

        public string ValidPasswordHash
        {
            get
            {
                DateTime l_dtNow = DateTime.Now;
                if (Passwords.Count != 1 || Passwords[0].ValidFrom >= l_dtNow || Passwords[0].ValidUntil <= l_dtNow)
                {
                    throw new Exception();
                }

                return Encoding.UTF8.GetString(Passwords[0].PasswordHash);
            }
        } // ValidPasswordHash

        public static User FindByCredentials(string i_sUsername, string i_sHashedPassword)
        {
            return FindOne(Restrictions.Eq("Username", i_sUsername), Restrictions.Eq("ValidPasswordHash", i_sHashedPassword));
        } // FindByCredentials
    } // User

    [ActiveRecord("_passwords")]
    public class Password : ActiveRecordBase<Password>
    {
        [PrimaryKey(PrimaryKeyType.Identity, "id", Access = PropertyAccess.NosetterLowercase)]
        public ulong Id
        {
            get;
            set;
        } // Id

        [BelongsTo("user_id", NotNull = true, UniqueKey = "_passwords_UQ1")]
        public ulong UserId
        {
            get;
            set;
        } // UserId

        [Property("password_hash", ColumnType = "UInt64", NotNull = true, UniqueKey = "_passwords_UQ1")]
        public byte[] PasswordHash
        {
            get;
            set;
        } // PasswordHash

        [Property("creation_date", ColumnType = "DateTime", NotNull = true)]
        public DateTime ValidFrom
        {
            get;
            set;
        } // ValidFrom

        [Property("valid_end_date", ColumnType = "DateTime", NotNull = true)]
        public DateTime ValidUntil
        {
            get;
            set;
        } // ValidUntil
    } // Password
} // DataEntities

and on my application start the framework is initialized

try
{
    ActiveRecordStarter.Initialize(ActiveRecordSectionHandler.Instance, typeof(Password),
                                                                        typeof(User));
}
catch (Exception l_excpt)
{
    // handle exception
}

At the end, when this code runs, it generates the following exception:

Castle.ActiveRecord.Framework.ActiveRecordException: ActiveRecord tried to infer details about the relation User.Passwords but it could not find a 'BelongsTo' mapped property in the target type DataEntities.Password
   at Castle.ActiveRecord.Framework.Internal.SemanticVerifierVisitor.VisitHasMany(HasManyModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\SemanticVerifierVisitor.cs:line 544
   at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitNodes(IEnumerable nodes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 45
   at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitModel(ActiveRecordModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 59
   at Castle.ActiveRecord.Framework.Internal.SemanticVerifierVisitor.VisitModel(ActiveRecordModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\SemanticVerifierVisitor.cs:line 122
   at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitNodes(IEnumerable nodes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 45
   at Castle.ActiveRecord.ActiveRecordStarter.RegisterTypes(ISessionFactoryHolder holder, IConfigurationSource source, IEnumerable`1 types, Boolean ignoreProblematicTypes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\ActiveRecordStarter.cs:line 927
   at Castle.ActiveRecord.ActiveRecordStarter.Initialize(IConfigurationSource source, Type[] types) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\ActiveRecordStarter.cs:line 202
   at Global.Application_Start(Object sender, EventArgs e) in C:\Projects Code\xyz\Global.asax.cs:line 22

Well, I have stared endlessly at the UserId property of the Password class, I have googled, and now I am quite lost. So the community is my last hope... Can anybody help me understanding what causes this exception and how to fix it?

Thank you all in advance for your replies and comments.

Était-ce utile?

La solution

You should have a User User { get; set; } reference property instead of a foreign key.

The official docs are a good place to start.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top