0
votes

As the title says I'm trying to inject some dependencies (for example domain services) into entities using NHibernate 5.2.6 integrated with Autofac 4.9.4. Suppose we want inject IPasswordHasher doamin service into UserAccount entity:

Example

public class UserAccount 
{
    public virtual Guid Id { get; set; }
    public virtual string EMail { get; set; }
    public virtual string HashedPassword { get; set; }

    private readonly IPasswordHasher _passwordHasher { get; set; }

    protected UserAccount() { }
    public UserAccount(IPasswordHasher passwordHasher)
    {
        _passwordHasher = passwordHasher;
    }

    public virtual void SetCredentials(
      string email, string plainTextPassword)
    {
        EMail = email;
        SetPassword(plainTextPassword);
    }

    public virtual void SetPassword(string plainTextPassword)
    {
        HashedPassword = _passwordHasher.HashPassword(
          EMail, plainTextPassword);
    }
}

I've already read this article but unfortunately this article is very old-fashioned and vague to me. In my last attempt I tried to set NHibernate.Cfg.Environment.BytecodeProvide using the Autofac.Extras.NHibernate NuGet package as follows:

Using Autofac.Extras.NHibernate

ContainerBuilder builder = new ContainerBuilder();

//Bootstrap the Autofac container
builder.RegisterType<PasswordHasher>().As<IPasswordHasher>();
builder.RegisterType<UserAccount>().AsSelf();
builder.RegisterType<StaticProxyFactoryFactory>().As<IProxyFactoryFactory>();
builder.RegisterType<DefaultCollectionTypeFactory>().As<ICollectionTypeFactory>();
builder.RegisterType<AutofacBytecodeProvider>().As<IBytecodeProvider>();
var container = builder.Build();

//Assign the BytecodeProvider to NHibernate
NHibernate.Cfg.Environment.BytecodeProvider = container.Resolve<IBytecodeProvider>();

Configuration nhConfig = new Configuration().Configure();
ConfigNHibernateAndAddMappings(nhConfig );

var SessionFactory = nhConfig.BuildSessionFactory();
var session = SessionFactory.OpenSession();

//entity._passwordHasher dependency is null when NHibernta warpped it with proxy
var entity= session.Query<UserAccount>().Where(x => x.EMail == "[email protected]").FirstOrDefault();

The Problem when there is no lazy loading, NHibernate doesn't warped queried entities with a proxy so in this case dependencies will be successfully injected. but when NHibernate warped the entity with a proxy, dependencies won't be injected and remain uninitialized ,any suggestion?

1
I suggest that you separate data and behavior. Put the code that requires the hasher in a different "service" or "function" or whatever. - Yacoub Massad
@YacoubMassad tnx for your suggestion but we need inject behaviors to entities. - Masoud Sedghi

1 Answers

1
votes

You should not inject dependencies into your aggregate roots.

You could use double dispatch but nowadays I lean more toward passing in values to the domain objects from an application/integration layer (concern).

Instead of:

public virtual void SetCredentials(
  string email, string plainTextPassword)
{
    EMail = email;
    SetPassword(plainTextPassword);
}

public virtual void SetPassword(string plainTextPassword)
{
    HashedPassword = _passwordHasher.HashPassword(
      EMail, plainTextPassword);
}

I would opt for:

public virtual void SetCredentials(
  string email, byte[] passwordHash)
{
    EMail = email;
    Password = passwordHash;
}

Even better would be to have that as the constructor.

Your application service would have the hashing implementation injected using traditional dependency injection and your data access implementation would not need to concern itself with dependencies.

This is in line with what @Yacoub Massad is suggesting. It is going to make your life somewhat simpler than even double dispatch.