I'm trying to play around with (what I think is) a factory that creates a repository depending on the enum being passed to the method. Looks like this:
RepositoryFactory
public class RepositoryFactory
{
public IRepository<IEntity> GetRepository(FormTypes formType)
{
// Represents the IRepository that should be created, based on the form type passed
var typeToCreate = formType.GetAttribute<EnumTypeAttribute>().Type;
// return an instance of the form type repository
IRepository<IEntity> type = Activator.CreateInstance(typeToCreate) as IRepository<IEntity>;
if (type != null)
return type;
throw new ArgumentException(string.Format("No repository found for {0}", nameof(formType)));
}
}
IRepository
public interface IRepository <T>
where T : class, IEntity
{
bool Create(IEnumerable<T> entities);
IEnumerable<T> Read();
bool Update(IEnumerable<T> entities);
bool Delete(IEnumerable<T> entities);
}
FormTypes
public enum FormTypes
{
[EnumType(typeof(Form64_9C2Repository))]
Form64_9C2,
[EnumType(typeof(Form64_9BaseRepository))]
Form64_9Base
}
EnumExtensions
public static class EnumExtensions
{
/// <summary>
/// Get the Enum attribute
/// </summary>
/// <typeparam name="T">The attribute</typeparam>
/// <param name="enumValue">The enum</param>
/// <returns>The type to create</returns>
public static T GetAttribute<T>(this System.Enum enumValue)
where T : Attribute
{
FieldInfo field = enumValue.GetType().GetField(enumValue.ToString());
object[] attribs = field.GetCustomAttributes(typeof(T), false);
T result = default(T);
if (attribs.Length > 0)
{
result = attribs[0] as T;
}
return result;
}
}
Form64_9C2Repository
public class Form64_9C2Repository : IRepository<Form64_9C2>
{
public bool Create(IEnumerable<Form64_9C2> entities)
{
throw new NotImplementedException();
}
public bool Delete(IEnumerable<Form64_9C2> entities)
{
throw new NotImplementedException();
}
public IEnumerable<Form64_9C2> Read()
{
throw new NotImplementedException();
}
public bool Update(IEnumerable<Form64_9C2> entities)
{
throw new NotImplementedException();
}
}
IEntity
public interface IEntity { }
Form64_9C2 (stub)
public class Form64_9C2 : IEntity { }
Calling it all as:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Repository Factory Example \n\n");
Business.Factory.RepositoryFactory factory = new Business.Factory.RepositoryFactory();
// Get a 64 9C2 repository
var repo9c2 = factory.GetRepository(FormTypes.Form64_9C2);
Console.WriteLine(repo9c2);
}
}
My problem is my type
is always resolving to null
. I'm expecting to get a NotImplementedException
, but am instead getting the ArgumentException
for not having a valid formType.
Prior to implementing IRepository<T>
my type
/repository
was successfully being created (working code here), any ideas? I'm only just getting started playing around with factories, generics, and the like - so if I'm doing something way wrong please advise!
Your code doesn't work for the exactly same reason for which this line doesn't compile:
IRepository<IEntity> repo = new Form64_9C2Repository();
Basically IRepository<IEntity>
is not the same as IRepository<Form64_9C2>
even if Form64_9C2
implements IEntity
.
This could have worked if the T
generic parameter on the IRepository
interface was covariant
:
public interface IRepository<out T> where T : class, IEntity
{
IEnumerable<T> Read();
}
But unfortunately this would mean that it can only appear as return type for the methods, not as parameter. Which is a no-go for your Update
, Delete
and Create
methods. You could of course define a structure like that:
public interface IReadonlyRepository<out T> where T : class, IEntity
{
IEnumerable<T> Read();
}
public interface IRepository<T>: IReadonlyRepository<T> where T : class, IEntity
{
bool Update(IEnumerable<T> entities);
bool Delete(IEnumerable<T> entities);
bool Create(IEnumerable<T> entities);
}
and have your GetRepository
method return an IReadonlyRepository<IEntity>
.
If this doesn't work for you you will need an additional parameter to specify the concrete entity type so that you perform the correct cast:
public IRepository<TEntity> GetRepository<TEntity>(FormTypes formType) where TEntity: class, IEntity
{
// Represents the IRepository that should be created, based on the form type passed
var typeToCreate = formType.GetAttribute<EnumTypeAttribute>().Type;
// return an instance of the form type repository
IRepository<TEntity> type = Activator.CreateInstance(typeToCreate) as IRepository<TEntity>;
if (type != null)
return type;
throw new ArgumentException(string.Format("No repository found for {0}", nameof(formType)));
}
}
and when calling in addition to specifying the repository type you will need to specify the entity type:
var repo9c2 = factory.GetRepository<Form64_9C2>(FormTypes.Form64_9C2);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With