[pMixins]

A lightweight open source Mixin and AOP framework for C#


Mixin Masks

The pMixin.Masks property allows you to specify a filter for which members will be mixed into the Target. This can be useful in a scenario when you have a single object that implements several interfaces, but you only want to expose a subset of those interfaces in the Target.

For example, in the Repository recipe, we introduced several CRUD interfaces. To continue this scenario say we wanted to be able to quickly build test repositories. We could build a simple in-memory generic repository that could be mixed into the entity specific implementations:

public class InMemoryRepository<T> : ICreate<T>, IReadById<T>, IReadAll<T>, IUpdate<T>, IDelete<T> where T : IEntity
{
   private readonly List<T> _dataStore = new List<T>();
   public bool Create(T entity)
   {
      _dataStore.Add(entity);
      return true;
   }
   public T ReadById(int id)
   {
      return _dataStore.FirstOrDefault(x => x.Id == id);
   }
   public IEnumerable<T> ReadAll()
   {
      return _dataStore.ToArray();
   }
   public bool Update(T entity)
   {
      Delete(entity);
      Create(entity);
      return true;
   }
   public bool Delete(T entity)
   {
      _dataStore.Remove(entity);
      return true;
   }
}

This looks good, until we start adding some business logic. Let's say we need to create a repository for MyEntity, but it should only be able to be Created and Read. It can't be Deleted or Updated, so we don't want these methods exposed via the Repository. Unfortunately, if we mixin in InMemoryRepository as is this will happen; additional method we don't want will be mixed in.

To solve this problem we can uee pMixin.Masks. By specifying only the interfaces we want to mixin, [pMixins] will restrict the members that are mixed in and which interfaces the Target will consequently implement:

[pMixin(Mixin = typeof(InMemoryRepository<MyEntity>), Masks = new[] {
   typeof(ICreate<MyEntity>),
   typeof(IReadById<MyEntity>)
})]
public partial class ExampleTestMyEntityRepository
{
}

So let's see it in action. In the test class below we create an instance of our repository. The CanWorkWithRepository test method verifies we can still access the ICreate and IReadById interfaces method. The RepositoryDoesNotImplementIUpdate test method verifies that the repository can not be cast to IUpdate, which verifies that the repository is not implementing this interface.

[TestFixture]
public class MixinMasksTest
{
   private readonly ExampleTestMyEntityRepository _repository = new ExampleTestMyEntityRepository();
   [Test]
   public void CanWorkWithRepository()
   {
      const int entityId = 5;
      //Add item to repository
      _repository.Create(new MyEntity {
         Id = entityId
      });
      //Read item back
      Assert.NotNull(_repository.ReadById(entityId));
   }
   [Test]
   public void RepositoryDoesNotImplementIUpdate()
   {
      Assert.Null(_repository as IUpdate<MyEntity>);
   }
}
Futher Thoughts

pMixin.Masks is not restricted to interfaces implemented by the Mixin, but it can be any interface. However, in this case, only inherited interfaces are considered, not members.

So this could be beneficial in a real world scenario. It would be common for the MyEntity repository to have a matching interface, so that consumers wouldn't be tightly coupled to a particular implementation. So we could have for example:

internal interface IExampleTestMyEntityRepository : ICreate<MyEntity>, IReadById<MyEntity>
{
}

In this case we could pass IExampleTestMyEntityRepository directly to pMixin.Masks:

[pMixin(Mixin = typeof(InMemoryRepository<MyEntity>), Masks = new[] {
   typeof(IExampleTestMyEntityRepository)
})]
public partial class AnotherTestMyEntityRepository : IExampleTestMyEntityRepository
{
}

Download the source for this and all other recipes Here!