0

I'm trying to make an mock data generator which will generate mocks based on a set of rules. I've been playing around with Bogus. I don't want to have to setup my rules for my entity classes each time, I would like to generically be able to apply rules to classes if they derrive from some interface.

Imagine I have entities which reuse a generic interface:

public interface IHasGeneric<T>
    where T : IHasGeneric<T>
{
    string Marker { get; set; }
}

public class Foo : IHasGeneric<Foo>
{
    public string Marker { get; set; }
}

public class Bar : IHasGeneric<Bar>
{
   public string Marker { get; set; }
}

Note: I'm aware this doesn't depict why I have a generic which takes in itself as a parameter. However, it takes too much to explain and cannot be changed from my architecture. So please work with it as a requirement.

Now I want to create a centralized Factory For Fakers, However I'm struggling to figure out how I can apply the rules generically to any type that is going to be generated.

public class MockDataGenerator
{
    public T Generate<T>()
        where T : class
    {
        var faker = new StatefulFaker<T>();
        this.ApplyDefaultRules<T>(faker);
    }

    public void ApplyDefaultRules<T>(StatefulFaker<T> faker)
        where T : class
    {
        //T Cannot be used as a type parameter 'T' ... No Implicit Conversion to IHasGeneric<T>
        if (typeof(T) is IHasGeneric<T>>)
        {

        }
    }
}

When trying to cast T to see if rules can be applied I get an error

T Cannot be used as a type parameter 'T' ... No Implicit Conversion to IHasGeneric. How can I generically apply rules to types which implement an interface?

johnny 5
  • 19,893
  • 50
  • 121
  • 195
  • Have you tried to add a type constraint to the method `ApplyDefaultRules`: `where T : class, IHasGeneric`? It is required, because you use type parameter `T` with interface `IHasGeneric` that declares a constraint `where T : IHasGeneric`. Is such solution acceptable for you or you don't want to specify such type constraint? – Iliar Turdushev Jun 05 '20 at 15:39
  • You can't use pattern matching on a generic type parameter like that. Take a look at [this](https://stackoverflow.com/a/4963190/10657522) answer to see how you can check if `T` implements your `IHasGeneric` interface. – Darren Ruane Jun 05 '20 at 15:40
  • @lilar I can't because T isn't guarenteed to implement that. But if it does implement it I would like to apply the rules – johnny 5 Jun 05 '20 at 15:43
  • Then check the link provided by @DarrenRuane. I think that this answer https://stackoverflow.com/a/4963190/12833205 solves your problem. – Iliar Turdushev Jun 05 '20 at 15:46
  • @DarrenRuane I would prefer to not have to go down the route of using reflection or else, all of the methods to apply must use reflection and so on. Is there another way in which I can implicitly assume the type? – johnny 5 Jun 05 '20 at 16:08
  • @johnny5 Why would you prefer to not have to go down the route of using reflection? As it stands, the only information your factory has is an object of type `Type`. To my knowledge, asides from passing in an object instance (and then using pattern matching to check for interface implementation), what you desire can not be achieved without using some reflection. – Darren Ruane Jun 05 '20 at 16:18
  • @DarrenRuane once I start using reflection, I'll have to use reflection the whole way though, e.g If I want to apply rules of (T) which derrives from Interface, then Instead of being able to use concrete expressions, I'll have to use reflection to proxy my call with the generic type. I'll do it if I have to, but then I probably end up having to write tests for my factory itself etc, it just increases the scope of the task a bit too much – johnny 5 Jun 05 '20 at 16:23
  • @johnny5 I understand your problem and I'm not sure there's a solution for it. To use concrete expressions in your Bogus rules, the `Faker` needs to use a concrete generic parameter (since it's a class and can't use a covariant generic parameter in its definition). This means even if you _could_ get the concrete type of `T`, you cannot then cast your `StatefulFaker` to `StatefulFaker` to specify concrete rule expressions for that type. – Darren Ruane Jun 05 '20 at 17:03
  • Are you able to add an additional interface without type constraints, for example, `interface IGeneric` and inherit `IHasGeneric` from it: `interface IHasGeneric : IGeneric where T : IHasGeneric`? And then use it in the method `ApplyDefaultRules`. Look at this sample: https://dotnetfiddle.net/dzZ9J6. Is such approach acceptable for you? – Iliar Turdushev Jun 05 '20 at 17:06
  • I can't change any of the base its 10 years old and tied into everything :/. I ended up applying everything through reflection. I just now have to write tests for my test class lol – johnny 5 Jun 05 '20 at 18:23

0 Answers0