0

Iam trying to make a generic class that receives a Type. this generic class will need to create an instance from the received type. The received type has two overloads in his constructor, one constructor can receive a parameter but the other one doesn't have any parameter. y generic class need sometimes to create object from received class without parameters in constructor and other times with parameter in constructor.

A simple view in my generic class :

public sealed class Repo<TContext> : IRepo<TContext>, IDisposable where TContext : DbContext, new() { #region properties /// <summary> /// Private DBContext property /// </summary> private DbContext _Context { get; } = null; /// <summary> /// Determine if Lazy Loading either activate or not /// </summary> private bool _LazyLoaded { get; set; } #endregion #region Construcors public Repo(bool LazyLoaded) { _Context = new TContext(); _LazyLoaded = LazyLoaded; _Context.ChangeTracker.LazyLoadingEnabled = LazyLoaded; } public Repo(DbContext context,bool LazyLoaded) { _Context = context; _LazyLoaded = LazyLoaded; _Context.ChangeTracker.LazyLoadingEnabled = LazyLoaded; } 

at now everything's good, but when I add a third constructor in my generic class for creating an instance from received TContext but this time with his (TContext) constructor that need one parameter,

public Repo(DbContextOptionsBuilder<TContext> optionsBuilder,bool LazyLoaded) { _Context = new TContext(optionsBuilder); _LazyLoaded = LazyLoaded; _Context.ChangeTracker.LazyLoadingEnabled = LazyLoaded; } 

I got this error:

Error CS0417 'TContext': cannot provide arguments when creating an instance of a variable type MyTypeName

The Question:

My question is how I can create an instance from TContext using his constructor that receive parameters ?

thank you in advance.

3
  • 1
    You can't use new() to do this. You could use reflection to create the new object, but that makes your code a little fragile. It would probably be better to pass in some kind of factory method that created the object. Commented Feb 7, 2021 at 16:45
  • As @DavidG says you're probably better off with a factory class although you'd need a factory class for each DbContext type you want to build. I'm wondering though... why do you need 3 ways to receive a DbContext? Something seems fishy in your setup. Commented Feb 7, 2021 at 16:49
  • Just a heads up even with the new() constraint, .net will use the following IL Code [System.Runtime]System.Activator::CreateInstance<!!0/*T*/>() Commented Feb 7, 2021 at 17:31

2 Answers 2

0

That doesn't work, at least not out of the box. There's no way to provide a constraint that indicates that the template argument must have a ctor with a specific signature (other than new(), which defines that the template argument must have a ctor with 0 arguments).

You could either provide the extra parameter as property instead (and add a constraint to a corresponding interface), or use reflection to do this:

_Context = Activator.CreateInstance(typeof(TContext), optionsBuilder); 

Of course, this has the downside of not being type safe any more. If TContext does not have a ctor taking the right argument type(s), you'd be getting an exception at runtime.

Seing the comment above, there's a third variant: Providing a factory interface:

public interface Creator<T> { T Create(); T Create(somearguments...); } 

And then you provide an instance of this interface to your Repo constructor.

Sign up to request clarification or add additional context in comments.

6 Comments

First off massive thanks sir for your time and effort, I tried _Context = Activator.CreateInstance(typeof(TContext), optionsBuilder);, but I got an exception : Constructor on type not found, please can you provide me a good solution with a simple example and I appreciate this for you
@XDev: To help you there, I would need to know the definition of the class you use as TContext. That one now needs to have a ctor with DbContextOptionsBuilder<TContext> optionsBuilder as argument.
Thinking about it... You might probably first check that this is even the right argument type for the context.
This is the definition for a dbcontext example public class TrainContext:DbContext { public TrainContext(DbContextOptions<TrainContext> contextOptions) : base(contextOptions) { } public TrainContext() { } #region DB Sets public virtual DbSet<Client> Client { get; set; } public virtual DbSet<Order> Order { get; set; } #endregion }
yeah I am sure that DbContextOptionsBuilder is the right argument for my contexts
|
0

One good option that does not involve reflection, is to use a factory functor/lambda.

public Repo( DbContextOptionsBuilder<TContext> optionsBuilder, bool LazyLoaded, Func<DbContextOptionsBuilder<TContext>, TContext> factory) { _Context = factory(optionsBuilder); _LazyLoaded = LazyLoaded; _Context.ChangeTracker.LazyLoadingEnabled = LazyLoaded; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.