NUnit, Unit - System.ArgumentException : An item with the same key has already been added.

1,384 views
Skip to first unread message

grandeconcarne

unread,
Apr 21, 2011, 3:04:15 AM4/21/11
to mongodb-user
Hello All,
I am using Unity for IoC and created a container extension to handle
mongo class mappings for the official c# driver. I am running into a
frustrating problem with my integration tests. The NUnit [SetUp] block
is responsible for setting up the container. Since [SetUp] gets called
per test and the BsonClassMap is a singleton I get the following
error: System.ArgumentException : An item with the same key has
already been added. I can wire up the container per test or catch that
error but, each of those paths I find distasteful. Any suggestions on
how to handle this? A .Clear() function perhaps?

Here is the Container Extension:
public class NewMongoDbContainerExtension :
UnityContainerExtension
{
protected override void Initialize()
{
RegisterTypes();
MapClasses();
}

private void RegisterTypes()
{
Container
.RegisterType<IAuthorityLogProvider,
AuthorityLogRepository>()
.RegisterType<IAuthorityLogRepository,
AuthorityLogRepository>()
.RegisterType<MongoDatabase>(new InjectionFactory((c)
=> CreateMongoDb()));
}

protected virtual MongoDatabase CreateMongoDb()
{
var settings =
ConfigurationManager.ConnectionStrings["someproject.mongodb"];
var server =
MongoServer.Create(settings.ConnectionString);
return server["someproject"];
}

private static void MapClasses()
{
BsonClassMap.RegisterClassMap<AuthorityLogEntryDto>(cm =>
{
cm.AutoMap();
cm.SetIdMember(cm.GetMemberMap(m =>
m.LogEntryId).SetIdGenerator(new CombGuidGenerator()));
});
}
}

Here is the TestFixture:
[TestFixture]
public class AuthorityLogPersistenceFixture
{
private IUnityContainer container;

[SetUp]
public void SetUp()
{
container = new UnityContainer()
.AddNewExtension<CoreContainerExtension>()
.AddNewExtension<NewMongoDbTestContainerExtension>();
}

[TearDown]
public void TearDown()
{
container.Resolve<MongoDatabase>().Drop();
container.Dispose();
}

[Test]
public void Can_persist_authority_log_entry()
{
var authorityLogRepository =
container.Resolve<IAuthorityLogRepository>();
authorityLogRepository.AddEntry(new
AddAuthorityLogEntryRequest
{
ipAddress = new byte[] { 255, 255, 255, 255 },
Result = false,
Info = "TEST"
});

int attempts = authorityLogRepository.NumberOfAttempts(new
NumberOfAttemptsQuery{
IpAddress = new byte[] { 255, 255, 255, 255 },
TimeSpan = new TimeSpan(0, -30, 0)
});

Assert.Greater(attempts, 0);
}

[Test]
public void Returns_expected_number_of_attempts()
{
var authorityLogRepository =
container.Resolve<IAuthorityLogRepository>();

for (int i = 0; i < 10; i++)
authorityLogRepository.AddEntry(new
AddAuthorityLogEntryRequest
{
ipAddress = new byte[] { 255, 255, 255, 255 },
Result = false,
Info = "TEST"
});

int attempts = authorityLogRepository.NumberOfAttempts(new
NumberOfAttemptsQuery{
IpAddress = new byte[] { 255, 255, 255, 255 },
TimeSpan = new TimeSpan(0, -30, 0)
});

Assert.AreEqual(10, attempts);
}
}

Best regards,
Keith

Robert Stam

unread,
Apr 21, 2011, 9:12:47 AM4/21/11
to mongodb-user
The registration of class maps is deliberately designed to be
something you do once at program startup. The reason for that is that
you want serialization to have a stable defined behavior for the
duration of your program run (you don't want objects serialized one
way for awhile and then differently after that).

The registration of class maps must happen:

1. Early in your program, certainly before any serialization/
deserialization involving that class occurs
2. Only once

A really good way to handle the "only once" requirement is to register
the class maps from a static constructor. That way the C# language
itself guarantees that they are only registered once. The best place
to put the static constructor is in the class itself. In your case, I
would recommend calling RegisterClassMap in the static constructor for
AuthorityLogEntryDto.

grandeconcarne

unread,
May 4, 2011, 2:26:58 PM5/4/11
to mongodb-user
Robert,
Thank you for such a quick response. Registering the class maps in the
static constructor works great for most cases. I have one situation
where I am specifying a custom HiLoIdGenerator (Ported from NoRM) that
gets built up by the UnityContainer. In that case I can't use the
static constructor to wire it up. It doesn't affect real usage but I
do have to account for the multiple class mappings when running unit
tests. I can wrap the entire block in a try /catch but I feel dirty
every time I look at it. For this scenario do you have any
suggestions? The following is an abbreviated version of my unity
container extension:

public class MongoDbContainerExtension : UnityContainerExtension
{
protected override void Initialize()
{
RegisterTypes();

try
{
MapClasses();
}
catch (ArgumentException) { }
}

private void RegisterTypes()
{
Container
.RegisterType<MongoDatabase>(new InjectionFactory((c)
=> CreateMongoDb()));
}

protected virtual MongoDatabase CreateMongoDb()
{
var settings =
ConfigurationManager.ConnectionStrings["connectionstring.key.name"];
var server =
MongoServer.Create(settings.ConnectionString);
return server["database.name"];
}

private void MapClasses()
{
BsonSerializer.RegisterIdGenerator(typeof(Guid), new
CombGuidGenerator());

BsonClassMap.RegisterClassMap<AuthorityLogEntryDto>(cm =>
{
cm.AutoMap();
cm.SetIdMember(cm.GetMemberMap(m => m.LogEntryId));
});

BsonClassMap.RegisterClassMap<UserDto>(cm =>
{
cm.AutoMap();
cm.SetIdMember(
cm.GetMemberMap(m => m.UserId)
.SetIdGenerator(

Container.Resolve<CollectionHiLoIdGenerator<long>>(
new ParameterOverrides {
{ "capacity", 20 },
{"collectionName", "users" }})));
});
}
}

Best regards,
Keith

On Apr 21, 6:12 am, Robert Stam <rstam10...@gmail.com> wrote:
> The registration of class maps is deliberately designed to be
> something you do once at program startup. The reason for that is that
> you want serialization to have a stable defined behavior for the
> duration of your program run (you don't want objects serialized one
> way for awhile and then differently after that).
>
> The registration of class maps must happen:
>
> 1. Early in your program, certainly before any serialization/
> deserialization involving that class occurs
> 2. Only once
>
> A really good way to handle the "only once" requirement is to register
> the class maps from a static constructor. That way the C# language
> itself guarantees that they are only registered once. The best place
> to put the static constructor is in the class itself. In your case, I
> would recommend calling RegisterClassMap in the static constructor for
> AuthorityLogEntryDto.
>
> On Apr 21, 3:04 am, grandeconcarne <grandeconca...@gmail.com> wrote:
>
>
>
>
>
>
>
> > Hello All,
> > I am usingUnityfor IoC and created a container extension to handle

Robert Stam

unread,
May 4, 2011, 2:42:17 PM5/4/11
to mongodb-user
You could add a static bool variable in your MongoDbContainerExtension
class to keep track of whether you have registered the class yet or
not.

The latest version of the driver on the master branch in github has a
new method called IsClassMapRegistered in BsonClassMap that you could
call to check whether you have already registered your class map or
not (I'm assuming you're not running multithreaded?).

grandeconcarne

unread,
May 4, 2011, 3:00:53 PM5/4/11
to mongodb-user
Thanks again Robert, i'll pull latest.

Regarding threading, the Container gets built up once within the
site's Application_Start handler and lives throughout the life of the
application, we haven't had any issues when load testing so I think we
are handling it properly. I am only having issues with the integration
tests.
Best regards,
Keith

On May 4, 11:42 am, Robert Stam <rstam10...@gmail.com> wrote:
> You could add a static bool variable in your MongoDbContainerExtension
> class to keep track of whether you have registered the class yet or
> not.
>
> The latest version of the driver on the master branch in github has a
> new method called IsClassMapRegistered in BsonClassMap that you could
> call to check whether you have already registered your class map or
> not (I'm assuming you're not running multithreaded?).
>
Reply all
Reply to author
Forward
0 new messages