Question

I am new to the dependency injection pattern and I am having issues getting a new instance of a class from container.Resolve in tinyioc it just keeps returning the same instance rather than a new instance. Now for the code

public abstract class HObjectBase : Object
{
    private string _name = String.Empty;
    public string Name 
    {
        get
        {
            return this._name;
        }
        set
        {
            if (this._name == string.Empty && value.Length > 0 && value != String.Empty)
                this._name = value;
            else if (value.Length < 1 && value == String.Empty)
                throw new FieldAccessException("Objects names cannot be blank");
            else
                throw new FieldAccessException("Once the internal name of an object has been set it cannot be changed");
        }
    }

    private Guid _id = new Guid();
    public Guid Id 
    {
        get
        {
            return this._id;
        }
        set
        {
            if (this._id == new Guid())
                this._id = value;
            else
                throw new FieldAccessException("Once the internal id of an object has been set it cannot be changed");

        }
    }

    private HObjectBase _parent = null;
    public HObjectBase Parent 
    {
        get
        {
            return this._parent;
        }
        set
        {
            if (this._parent == null)
                this._parent = value;
            else
                throw new FieldAccessException("Once the parent of an object has been set it cannot be changed");
        }
    }
}

public abstract class HZoneBase : HObjectBase
{
    public new HObjectBase Parent
    {
        get
        {
            return base.Parent;
        }
        set
        {
            if (value == null || value.GetType() == typeof(HZoneBase))
            {
                base.Parent = value;
            }
            else
            {
                throw new FieldAccessException("Zones may only have other zones as parents");
            }
        }
    }


    private IHMetaDataStore _store;
    public HZoneBase(IHMetaDataStore store)
    {
        this._store = store;
    }



    public void Save()
    {
        this._store.SaveZone(this);
    }
}

And the derived class is a dummy at the moment but here it is

public class HZone : HZoneBase
{
    public HZone(IHMetaDataStore store)
        : base(store)
    {
    }
}

Now since this is meant to be an external library I have a faced class for accessing everything

public class Hadrian 
{
    private TinyIoCContainer _container;

    public Hadrian(IHMetaDataStore store)
    {
        this._container = new TinyIoCContainer();
        this._container.Register(store);
        this._container.AutoRegister();
    }

    public HZoneBase NewZone()
    {
        return _container.Resolve<HZoneBase>();
    }

    public HZoneBase GetZone(Guid id)
    {
        var metadataStore = this._container.Resolve<IHMetaDataStore>();
        return metadataStore.GetZone(id);
    }

    public List<HZoneBase> ListRootZones()
    {
        var metadataStore = this._container.Resolve<IHMetaDataStore>();
        return metadataStore.ListRootZones();
    }
}

However the test is failing because the GetNewZone() method on the Hadrian class keeps returning the same instance.

Test Code

[Fact]
public void ListZones()
{
    Hadrian instance = new Hadrian(new MemoryMetaDataStore());
    Guid[] guids = { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
    int cnt = 0;
    foreach (Guid guid in guids)
    {
        HZone zone = (HZone)instance.NewZone();
        zone.Id = guids[cnt];
        zone.Name = "Testing" + cnt.ToString();
        zone.Parent = null;
        zone.Save();
        cnt++;
    }

    cnt = 0;
    foreach (HZone zone in instance.ListRootZones())
    {
        Assert.Equal(zone.Id, guids[cnt]);
        Assert.Equal(zone.Name, "Testing" + cnt.ToString());
        Assert.Equal(zone.Parent, null);
    }
}

I know its probably something simple I'm missing with the pattern but I'm not sure, any help would be appreciated.

Was it helpful?

Solution

First, please always simplify the code to what is absolutely necessary to demonstrate the problem, but provide enough that it will actually run; I had to guess what MemoryMetaDataStore does and implement it myself to run the code.

Also, please say where and how stuff fails, to point others straight to the issue. I spent a few minues figuring out that the exception I was getting was your problem and you weren't even getting to the assertions.

That said, container.Resolve<HZoneBase>() will always return the same instance because that's how autoregistration in TinyIoC works - once an abstraction has been resolved, the same instance is always returned for subsequent calls.

To change this, add the following line to the Hadrian constructor:

this._container.Register<HZoneBase, HZone>().AsMultiInstance();

This will tell the container to create a new instance for each resolution request for HZoneBase.

Also, Bassetassen's answer about the Assert part is correct.

In general, if you want to learn DI, you should read Mark Seemann's excellent book "Dependency Injection in .NET" - not quite an easy read as the whole topic is inherently complex, but it's more than worth it and will let you get into it a few years faster than by learning it on your own.

OTHER TIPS

In your assert stage you are not incrementing cnt. You are also using the actual value as the expected one in the assert. This will be confusing, becuase it says something is excpected when it actually is the actual value that is returned.

The assert part should be:

cnt = 0;
foreach (HZone zone in instance.ListRootZones())
{
    Assert.Equal(guids[cnt], zone.Id);
    Assert.Equal("Testing" + cnt.ToString(), zone.Name);
    Assert.Equal(null, zone.Parent);
    cnt++;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top