Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/MongoFramework/Attributes/MappingAdapterAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Runtime.InteropServices;
using MongoFramework.Infrastructure.Mapping;

namespace MongoFramework.Attributes
{
/// <summary>
/// Allows an IMappingProcessor to override definitions in code. Runs after attribute processing, so the adapter can override attributes.
/// Adapter type must have a parameterless constructor.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MappingAdapterAttribute : Attribute
{
/// <summary>
/// Gets the adapter type for the attached class
/// </summary>
public Type MappingAdapter { get; }

public MappingAdapterAttribute(Type adapterType)
{
if (!typeof(IMappingProcessor).IsAssignableFrom(adapterType))
{
throw new ArgumentException("Mapping Adapter Type must implement IMappingProcessor", nameof(adapterType));
}

MappingAdapter = adapterType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public static class DefaultProcessors
new DecimalSerializationProcessor(),
new TypeDiscoveryProcessor(),
new BsonKnownTypesProcessor(),
new IndexProcessor()
new IndexProcessor(),
new MappingAdapterProcessor()
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Text;
using MongoDB.Bson.Serialization;
using MongoFramework.Attributes;

namespace MongoFramework.Infrastructure.Mapping.Processors
{
public class MappingAdapterProcessor : IMappingProcessor
{
public void ApplyMapping(IEntityDefinition definition, BsonClassMap classMap)
{
var adapterAttribute = definition.EntityType.GetCustomAttribute<MappingAdapterAttribute>();

if (adapterAttribute == null)
{
return;
}

var instance = (IMappingProcessor)Activator.CreateInstance(adapterAttribute.MappingAdapter);

if (instance != null)
{
instance.ApplyMapping(definition, classMap);
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.IdGenerators;
using MongoFramework.Attributes;
using MongoFramework.Infrastructure.Mapping;
using MongoFramework.Infrastructure.Mapping.Processors;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace MongoFramework.Tests.Infrastructure.Mapping.Processors
{
[TestClass]
public class MappingAdapterProcessorTests : MappingTestBase
{

public class AdapterTestModelMappingAdapter : IMappingProcessor
{
public void ApplyMapping(IEntityDefinition definition, BsonClassMap classMap)
{
definition.CollectionName = "Custom";

var definitionIndexes = definition.Indexes.ToList();

definitionIndexes.Add(new EntityIndex
{
Property = definition.GetProperty("UserName"),
IsUnique = true,
SortOrder = IndexSortOrder.Ascending
});

definition.Indexes = definitionIndexes;
}
}

[Table("TestModels")]
[MappingAdapter(typeof(AdapterTestModelMappingAdapter))]
public class AdapterTestModel
{
public Guid MyCustomId { get; set; }
public string UserName { get; set; }

}

public class AdapterTestModelMappingAdapterNoInterface
{
// no interface
}

[MappingAdapter(typeof(AdapterTestModelMappingAdapterNoInterface))]
public class AdapterTestModelNoInterface
{
// broken adapter
}

public class AdapterTestModelMappingAdapterConstructor : IMappingProcessor
{
public AdapterTestModelMappingAdapterConstructor(string test)
{

}

public void ApplyMapping(IEntityDefinition definition, BsonClassMap classMap)
{
throw new NotImplementedException();
}
}

[MappingAdapter(typeof(AdapterTestModelMappingAdapterConstructor))]
public class AdapterTestModelConstructor
{
// broken adapter
}

[TestMethod]
public void AdapterRequiresIMappingProcessor()
{
EntityMapping.AddMappingProcessor(new CollectionNameProcessor());
EntityMapping.AddMappingProcessor(new PropertyMappingProcessor());
EntityMapping.AddMappingProcessor(new EntityIdProcessor());
EntityMapping.AddMappingProcessor(new MappingAdapterProcessor());
Assert.ThrowsException<ArgumentException>(() => EntityMapping.RegisterType(typeof(AdapterTestModelNoInterface)));
}

[TestMethod]
public void AdapterRequiresParameterlessConstructor()
{
EntityMapping.AddMappingProcessor(new CollectionNameProcessor());
EntityMapping.AddMappingProcessor(new PropertyMappingProcessor());
EntityMapping.AddMappingProcessor(new EntityIdProcessor());
EntityMapping.AddMappingProcessor(new MappingAdapterProcessor());
Assert.ThrowsException<MissingMethodException>(() => EntityMapping.RegisterType(typeof(AdapterTestModelConstructor)));
}

[TestMethod]
public void AdapterOverridesAttributes()
{
EntityMapping.AddMappingProcessor(new CollectionNameProcessor());
EntityMapping.AddMappingProcessor(new PropertyMappingProcessor());
EntityMapping.AddMappingProcessor(new EntityIdProcessor());
EntityMapping.AddMappingProcessor(new MappingAdapterProcessor());
var definition = EntityMapping.RegisterType(typeof(AdapterTestModel));

Assert.AreEqual("Custom", definition.CollectionName);
Assert.AreEqual(1, definition.Indexes.Count());



}

}
}