Question

I have a domain layer assembly which contains the entities and it is referenced by a data layer assembly which contains the entity repositories implementation. In the domain layer assembly, entities are organized in aggregates which have root entities and sub entities. I made sub entities not directly instantiable turning their constructors to internal. In this way clients cannot use the new operator on them but they are forced to use factories.

In the data layer assembly I have a repository for each entity (regardless the fact it is a root or a sub entity). Repositories are implemented using generics and the problem rises when in the GetById and in GetAll methods which returns actual entities or list of entities. To have them create new instances, I have to specify the new() constraint on the generics. This won't compile because new() constraint requires public parameter-less constructor. I tried to use the internalsVisibleTo attribute to allow the data layer assembly having access to the domain layer internals but it didn't work.

I am not using EF or other ORM but ADO.NET with a very simple mapping library.

This is some example code:

namespace DomainLayer {

    public class Entity {

        internal Entity() {
        }      

    }

}

namespace DataLayer {

    public class Repository<T> where T: new() {

        public T GetById<T>() {
            return new T();
        }

    }

}

namespace Client {

    public class AClientClass {

        public void aMethod() {
            Entity entity1 = new Entity(); // Not possible, correct
            Entity entity2 = new Repository<Entity>().GetById(10); //Not possible, how to fix it???
        }

    }

}

The compile error I am getting is:

'DomainLayer.Entity' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'DataLayer.Repository<T>'    

Many thanks. Seb

Using Ed_Chapel suggestion below the refactored code:

namespace DomainLayer {

    public class Entity {

        internal Entity() {
        }      

    }

}

namespace DataLayer {

    public class Repository<T> {

        private Func<T> _creator;

        public Repository(Func<T> creator) {
            _creator = creator;
        }

        public T GetById(int id) {
            return _creator();
        }

    }
}

namespace DataLayer {
    public class EntityRepository : Repository<Entity> {

        public EntityRepository()
            : base(() => new Entity()) {
        }

    }
}

namespace Client {

    public class AClientClass {

        public void aMethod() {
            Entity entity1 = new Entity(); // Not possible, correct
            Entity entity2 = new EntityRepository().GetById(10); //Now working!!!
        }

    }

}

Thanks To Ed_Chapel!

Was it helpful?

Solution

Assuming the clients are not instantiating the Repository<T> and you are providing the instances, you could pass a Func<T> to do the activation for you.

public class Repository<T> {

    private Func<T> _activator;

    internal Repository<T>(Func<T> activator) {
        _activator = activator;
    }

    public T GetById<T>() {
        return _activator();
    }
}

You would then create your Repository<T> internally.

EntityRepository = new Repository<Entity>(() => new Entity());

Another advantage here is that if some entities have a non-empty .ctor, your Func<T> can accommodate that.

OTHER TIPS

To get around requiring a Repository<T> for every type accessed through the Repository, you might look at System.Activator -- assuming:

  1. you know what the parameters of the constructor are; and
  2. all constructors of T have the same signature.

Example:

return (T)Activator.CreateInstance(typeof(T), new object[] { id, array, whatever });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top