Migrating ZF2 to ZF3: Hot to convert the "realServiceLocator"? - zend-framework2

In the Factory classes of my Zend Framework 2 application, I often used such a construction:
// The signature was actually wrong, since it was always the `AbstractPluginManager` (or the `Zend\ServiceManager\ServiceManager` for the "common" services) and not just `ServiceLocator`, and it also was used as `AbstractPluginManager` (or `ServiceManager` for the "common" services). The `ServiceLocatorInterface` did not provide the `getServiceLocator()` method.
public function createService(ServiceLocatorInterface $serviceLocator)
{
// the common ServiceLocator
$realServiceLocator = $serviceLocator->getServiceLocator();
$myServiceFoo = $realServiceLocator->get('My\Service\Foo');
$myServiceBar = new \My\Service\Bar($myServiceFoo);
...
}
So to access a "common" service, I first retrieved the ServiceLocator. This approach was necessary in factories for Hydrators, Controllers, and other services, that have their own ServiceManagers. Because for them the input ServiceLocator was AbstractPluginManager and not the Zend\ServiceManager\ServiceManager.
Now I made the first migration step for my factories and replaced some common things:
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// the common ServiceLocator
$realServiceLocator = $container->getServiceLocator();
$myServiceFoo = $realServiceLocator->get('My\Service\Foo');
$myServiceBar = new \My\Service\Bar($myServiceFoo);
...
}
How to adapt the $container->getServiceLocator() to the ZF3?

getServiceLocator() is deprecated so you can not use this one in ZF3. You would be able to make the service manager available using an instance of Interop\Container\ContainerInterface while making factories as the following
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$myServiceFoo = $container->get('My\Service\Foo');
$myServiceBar = new \My\Service\Bar($myServiceFoo);
...
}

Related

How to use my custom google calendar module into another custom todo module in Laminas MVC?

I've created a Calendar module in laminas MVC which interacts with Google Calendar and then created another Todo module which is supposed to interact with my Calendar module. The signature of CalendarController in Calendar module is like
public function __construct(
ListProcess $listProcess,
AddProcess $addProcess,
EditProcess $editProcess,
DeleteProcess $deleteProcess
)
Now my code in Todo module that is supposed to initiate the scheduling process is as below
public function execute(): array
{
$todo = json_decode((new CrmApiService())->getTodo($this->getTodoId()), true);
$eventData["summary"] = $todo->title;
$eventData["description"] = $todo->content;
$eventData["startDateTime"] = $todo->nextActionDate;
$eventData["endDateTime"] = $todo->nextActionDate;
$calendar = new CalendarController();
return $calendar->scheduleFromAnotherSource($eventData);
}
when I execute this, I get an error like below
Too few arguments to function CalendarModule\Controller\CalendarController::__construct(), 0 passed in D:\laminas\todo-module-integrated\vendor\iss-module\todo-module\src\Process\TodoScheduleProcess.php on line 53 and exactly 4 expected
I know that I'm not supposed to call the CalendarController directly rather it should be through a Service.
My question is, how should I create a Service in Todo module that has the dependency on Calendar module and it should interact with Calendar module without requiring the involvement of CalendarController which has further dependencies?
Thanks for all the help.
Here's how I've implemented it. (May it'll help someone)
In my Calendar-module, the logic of adding is separate from CalendarController and its called AddProcess, this is how I add an event from the controller. $result = $this->addProcess->execute($this->params()->fromPost());. The Google authentication is being handled through a separate service CalendarClientService. All my processes access this service to authenticate as below and then get executed.
$client = $this->calendarClientService->getClient();
if (!$this->calendarClientService->authenticateClient($client)) {
return ["error" => "authentication", "url" => filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL)];
}
Now I've created a new service in Calendar-module as below where I just called AddProcess and passed it the new eventData.
class CalendarService
{
protected AddProcess $addProcess;
public function __construct(AddProcess $addProcess)
{
$this->addProcess = $addProcess;
}
public function scheduleAsEvent($eventData)
{
$eventData["startDateTime"] = Carbon::parse($eventData["startDateTime"])->format("Y-m-d\TH:i");
$eventData["endDateTime"] = Carbon::parse($eventData["endDateTime"])->format("Y-m-d\TH:i");
return $this->addProcess->execute($eventData);
}
}
Then from my Todo-module, I access this service as below
namespace TodoModule\Process;
use Carbon\Carbon;
use Google\Exception;
use Laminas\Cache\Exception\ExceptionInterface;
use Laminas\Mvc\Controller\AbstractActionController;
use CalendarModule\Service\CalendarService;
use TodoModule\Service\CrmApiService;
class TodoScheduleProcess extends AbstractActionController
{
protected int $todoID;
protected CalendarService $calendarService;
public function __construct(CalendarService $calendarService)
{
$this->calendarService = $calendarService;
}
public function execute(): array
{
$todo = json_decode((new CrmApiService())->getTodo($this->getTodoId()));
$eventData["summary"] = $todo->title;
$eventData["description"] = $todo->content;
$eventData["startDateTime"] = $todo->nextActionDate;
$eventData["endDateTime"] = $todo->nextActionDate;
return $this->calendarService->scheduleAsEvent($eventData);
}
public function getTodoId()
{
return $this->todoID;
}
public function setTodoId($id)
{
$this->todoID = $id;
}
}```

zend framework 2 understanding the exchangeArray method

From the documentation:
namespace Album\Model;
class Album
{
public $id;
public $artist;
public $title;
public function exchangeArray($data)
{
$this->id = (!empty($data['id'])) ? $data['id'] : null;
$this->artist = (!empty($data['artist'])) ? $data['artist'] : null;
$this->title = (!empty($data['title'])) ? $data['title'] : null;
}
}
Our Album entity object is a simple PHP class. In order to work with Zend\Db’s TableGateway class, we need to implement the exchangeArray() method. This method simply copies the data from the passed in array to our entity’s properties
Ok, we need to. But what's the pourpose of that function?
I mean, I've understood what that function does but I can't understand why it does things in that way.
Is it really necessary to declare all the variables?
Let's say I have a table of 20 columns and I want to select them all.
Then I should declare 20 named variables.
That makes sense if I want to distinguish between public (to print) and private (internal) variables.
Is there any other reason?
It 's not just about defining class members. It 's more about object orientated benefits like encapsulation, inheritance, etc.
Let 's assume your entity looks like this:
declare(strict_types=1);
namespace Application\Entity;
class Album
{
protected $id;
protected $artist;
protected $title;
public function getId() : int
{
return $this->id;
}
public function setId(int $id) : Album
{
$this->id = $id;
return $this;
}
public function getArtist() : string
{
return $this->artist;
}
public function setArtist(string $artist) : Album
{
$this->artist = $artist;
return $this;
}
public function getTitle() : string
{
return $this->title;
}
public function setTitle(string $title) : Album
{
$this->title = $title;
return $this;
}
}
First advantage using entities: there is no possibility to make typos. $data['atrist'] = 'Marcel' will work in most cases. $album->setAtrist('Marcel') will throw an error.
Second advantage is type hinting. Especially when you 're using PHP7 you can use the advantage of type hinting. $album->setId('1') will throw an error because this method expects an integer value.
Third advantage is the possibility of adding some extra code to your entity. what if we need a release date and no release date is given? You can kind of validate things in entities.
protected $releaseDate;
public function getReleaseDate() : \DateTime
{
if ($this->releaseData == null) {
throw new \Exception('no release date given. evacuate!');
}
return $this->releaseDate;
}
Another advantage is hydration in zend framework. Although the exchangeArray method is a kind of simple hydration, zend framework offers way more complex ways of hydration. What, if your release date column in the database table is of type DATE and you want your releaseDate member in your entity to be a \DateTime object representing this date?
// data from your database
$data = [
'id' => 1,
'artist' => 'the outside agency',
'title' => 'scenocide 202',
'releaseDate' => '2010-06-30',
];
// hydration of your entity with zend 's own hydrator classes
$album = (new ClassMethods())
->addStrategy('releaseDate', new DateTimeStrategy('Y-m-d'))
->hydrate($data, new Album());
$releaseDate = $album->getReleaseDate()->format('d.m.Y');
As you can see the release date was a simple string. While hydrating your entity, the release date will be transformed to a \DateTime object through a hydrator strategy.
These benefits are way more than distinguish between public, protected and private variables. An entity only takes and gives variables, that should be in your entity. You can use all the oo things like inheritance (implementing the \JsonSerializable interface is pretty magic sometimes), type hinting, encapsulation, polymorphism and so on ...
Last but not least: IDE support. If your entity object is strictly php doc commented, your IDE knows what you can do with your entity. Less work for you. ;)
Edit: Table Gateway instantiation with hydrating resultset
To use the above described advantges of entity objects with hydrators in a table gateway, you have to instantiate the table gateway like in the following example.
class AlbumTableGateway extends TableGateway
{
public function __construct(Adapter $adapter)
{
$resultset = new HydratingResultset(
(new ClassMethods())->addStrategy('releaseDate', new DateTimeFormatter()),
new AlbumEntity()
);
parent::__construct('album_table', $adapter, null, $resultset);
}
public function fetchById($id)
{
$select = $this->getSql()->select();
$select->columns([
'id',
'artist',
'title',
'releaseDate',
]);
$select->where->equalTo('id', $id);
$result = $this->selectWith($select);
// get the found resultset with $result->current()->getId();
return $result;
}
}
This example assumes that the Table Gateway is created via a corresponding factory.

Are Factories Using An IoC Container A Service Locator?

Lets say I have a factory returning different classes via methods.
class CarFactory
{
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function createCarOne() : CarInterface
{
return $this->container->make(CarOneClass::class);
}
// Vs
public function createCarTwo() : CarInterface
{
return new CarTwoClass({Inject Dependencies Here});
}
}
When would this be considered a service locator or anti-pattern and why? I am considering the first method solely for the dependency resolution provided by the container. All car's have the same typed interface dependencies the main difference of the entities come from how they transform the data provided.
Whenever one of these methods are called I need a new instance of the specified car so the data set can be transformed based on the choice.
This is not the implementation but the easiest example I can provide.
$output = [];
foreach ($car as $key => $data) {
$newCar = $this->factory->createCar{$key}();
// Pass Some Data To The New Car Methods So It Can Be Transformed
$output[] = $newCar;
}
return $output;
If this is the wrong approach what would be the alternative option?
Edit
After further digging I see some IoC containers pass factory callables as dependencies. I was going to bind each Car to a callable but thanks to the ability to type hint data from method returns (php7) I can configure factories using a provider then call the 'callable factory' from within the CarFactory. Requires additional binding but prevents the need to reference/dependency inject the IoC container within every factory.
Still researching I would love to hear feedback from those with more experience.
Ex:
// Within Some Registered Provider
// I Will Have To Wire Each Car
$one = function() use ($app) {
return $app->make(CarOne::class);
};
$two = function() use ($app) {
return $app->make(CarTwo::class);
};
$app->bind(ICarFactory::class, function($app) use ($one, $two) {
return $app->make($concrete, [$one, $two]);
});
// Car Factory Constructor
public function __construct(callable $carOne, callable $carTwo) {
$this->one = $carOne;
$this->two = $carTwo;
}
Since get methods are type hinted ( view original car factory ) an error is thrown when the returned item does not implement CarInterface, each factory method would just have to call the 'callable factory' ( something like this return ($this->one)();).
I believe i solve my problem of outsourcing creation of dependencies ( avoiding creating within factory was bothering the hell out of me ) while still following 'best practices'. Still looking for advice if anyone has any to offer.

Net Core Dependency Injection for Non-Controller

Seems crazy that something like this is causing me such a headache. But here it is:
How do you use the built-in dependency injection for net core for a non-controller class? Please provide an example with includes instantiation.
Thanks.
Just make the class a service.
In startup.cs
services.AddScoped<AccountBusinessLayer>();
Then in controller, same as you do for other services:
private readonly AccountBusinessLayer _ABL;
Include in constructor as you do for other services:
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,IOptions<IdentityCookieOptions> identityCookieOptions,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory,
RoleManager<IdentityRole> roleManager,
AccountBusinessLayer ABL
)
{
_userManager = userManager;
_signInManager = signInManager;
_externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<AccountController>();
_roleManager = roleManager;
_ABL = ABL;
}
You can easily define a static class with one property like:
public static class StaticServiceProvider
{
public static IServiceProvider Provider { get; set; }
}
after defined class you have to scope the service in the Startup.ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//TODO: ...
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddSingleton<ISomeInterface, ISomeImplementation>();
}
then inside the Startup.Configure method on startup you can set the provider as static class property:
public void Configure(IApplicationBuilder app, ...)
{
StaticServiceProvider.Provider = app.ApplicationServices;
//TODO: ...
}
Now you can easily call StaticServiceProvider.Provider.GetService method almost everywhere in your application:
var unitOfWork = (IUnitOfWork)StaticServiceProvider.Provider.GetService(typeof(IUnitOfWork));
I'm not sure this is the best answer, but the way I decided to do it is to do the following:
1) Per the answer by #BrunoLM at on this question Resolving instances with ASP.NET Core DI suggested by #SystemCrash, I created a new project called UnderstandingDependencyInjection and pasted in the code examples.
Important: What I describe next see next will not make sense unless you visit the referenced link above (#1). What you see below is a partial solution that builds on the answer another user provided in a another SO question.
2) Next, I created another class called OtherService. I added a method DoSomething() that took a dependency on the TestService.
3) In the constructor of OtherService, I requested IServiceProvider in order to get a concrete implementation of ITestService so I could call its GenerateRandom() method.
4) Back in the HomeController.cs, I merely passed along the IServiceProvider reference to the constructor of OtherService.
So, this is what I have:
OtherService.cs
using System;
using Microsoft.Extensions.DependencyInjection;
namespace UnderstandingDependencyInjection.Services
{
public class OtherService
{
private readonly ITestService _testService;
public OtherService(IServiceProvider serviceProvider)
{
_testService = serviceProvider.GetService<ITestService>();
}
public int DoSomething()
{
var rnd = _testService.GenerateRandom();
return rnd * 2;
}
}
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
using UnderstandingDependencyInjection.Services;
namespace UnderstandingDependencyInjection.Controllers
{
public class HomeController : Controller
{
private readonly ITestService _testService;
private readonly IServiceProvider _serviceProvider;
public HomeController(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_testService = serviceProvider.GetService<ITestService>();
}
public IActionResult Index()
{
// This works!
// var rnd = _testService.GenerateRandom();
// What if I need to reference the TestService
// from another service? I.e., OtherService?
var otherService = new OtherService(_serviceProvider);
var rnd = otherService.DoSomething();
ViewBag.RandomNumber = rnd;
return View();
}
So, to summarize, the key to this technique is to pass around the concrete reference of IServiceProvider that your controller receives ... passing from the controller into any other custom classes that will also need any services that are registered into ASP.NET Core's DI framework.
What about static methods that depend on the TestService?
But, I may not want / need to create an instance of OtherService. I may want to merely call a method statically, but that method takes a dependency on a service managed by ASP.NET Core MVC's Dependency Injection framework. What now?
In this case, the best I can figure out, you would need to pass in the reference ON THE METHOD CALL to the static method. It looks nasty, and I'm hoping there's a more elegant way ... but here's what I figured out.
5) Building on the previous steps (above) I added a new class called StaticService.
6) I created a method DoSomething that takes IServiceProvider as a parameter.
7) I use the concrete instance of the IServiceProvider to get a concrete instance of the ITestService. I use this to call GenerateRandom().
8) From the controller, call the StaticService.DoSomething() method passing it the concrete instance of IServiceProvider that I'm holding on to.
StaticService.cs
using Microsoft.Extensions.DependencyInjection;
namespace UnderstandingDependencyInjection.Services
{
public class StaticService
{
// No constructors
public static int DoSomething(IServiceProvider serviceProvider)
{
var testService = serviceProvider.GetService<ITestService>();
var rnd = testService.GenerateRandom();
return rnd * 3;
}
}
}
HomeController.cs
public IActionResult Index()
{
// This works!
// var rnd = _testService.GenerateRandom();
// What if I need to reference the TestService
// from another service? I.e., OtherService?
//var otherService = new OtherService(_serviceProvider);
//var rnd = otherService.DoSomething();
// What if I need to reference the TestService
// from another service with a STATIC method?
// Best I can tell, you have to pass the
// ServiceProvider in on the method call.
var rnd = StaticService.DoSomething(_serviceProvider);
ViewBag.RandomNumber = rnd;
return View();
}
But isn't passing around ServiceProvider an anti-pattern?
In short, yes. You wind up passing ServiceProvider around everywhere in code. Some would argue that this gives every controller and ever class access to every service registered in ASP.NET Core's DI. That's true, and that seems bad.
But what are your alternatives? Should every class that has a dependency on your service ALSO be defined as a service and registered with the DI? In other words, should I create IOtherService, and then pass it a concrete ITestService in its constructor?
I could do that, HOWEVER now my controller's constructor needs BOTH ITestService AND IOtherService. In other words, in order to work correctly, the Controller needs to know how OtherService does its job and that it uses ITestService internally. That seems bad, too.
What to do?
What's the Best Answer?
Frankly, I think the best answer is found here:
Passing Services using Dependency Injection and Factory Pattern in ASP.NET
#Steven says in his answer:
It does mean however that you might need to move away from the built-in DI container of ASP.NET Core to a more feature rich DI library, because the built-in container is not capable of making a context aware registration for ILogger while having the library auto-wire other constructor dependencies as well.
There are actually many ways to inject your dependency, the most common one you will find on controllers. There is also this variant
var someService = (ISomeService)HttpContext.RequestServices.GetService(typeof(ISomeService));

NewtonSoft json Contract Resolver with MVC 4.0 Web Api not producing the output as expected

I am trying to create a conditional ContractResolver so that I can control the serialization differently depending on the web request/controller action.
For example in my User Controller I want to serialize all properties of my User but some of the related objects I might only serialize the primitive types. But if I went to my company controller I want to serialize all the properties of the company but maybe only the primitive ones of the user (because of this I don't want to use dataannotations or shouldserialize functions.
So looking at the custom ContractResolver page i created my own.
http://james.newtonking.com/projects/json/help/index.html?topic=html/ContractResolver.htm
It looks like this
public class IgnoreListContractResolver : DefaultContractResolver
{
private readonly Dictionary<string, List<string>> IgnoreList;
public IgnoreListContractResolver(Dictionary<string, List<string>> i)
{
IgnoreList = i;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
List<JsonProperty> properties = base.CreateProperties(type, memberSerialization).ToList();
if(IgnoreList.ContainsKey(type.Name))
{
properties.RemoveAll(x => IgnoreList[type.Name].Contains(x.PropertyName));
}
return properties;
}
}
And then in my web api controller action for GetUsers i do this
public dynamic GetUsers()
{
List<User> Users = db.Users.ToList();
List<string> RoleList = new List<string>();
RoleList.Add("UsersInRole");
List<string> CompanyList = new List<string>();
CompanyList.Add("CompanyAccesses");
CompanyList.Add("ArchivedMemberships");
CompanyList.Add("AddCodes");
Dictionary<string, List<string>> IgnoreList = new Dictionary<string, List<string>>();
IgnoreList.Add("Role", RoleList);
IgnoreList.Add("Company", CompanyList);
GlobalConfiguration
.Configuration
.Formatters.JsonFormatter
.SerializerSettings
.ContractResolver = new IgnoreListContractResolver(IgnoreList);
return new { List = Users, Status = "Success" };
}
So when debugging this I see my contract resolver run and it returns the correct properties but the Json returned to the browser still contains entries for the properties I removed from the list.
Any ideas what I am missing or how I can step into the Json serialization step in webapi controllers.
*UPDATE**
I should add that this is in an MVC4 project that has both MVC controllers and webapi controllers. The User, Company, and Role objects are objects (created by code first) that get loaded from EF5. The controller in question is a web api controller. Not sure why this matters but I tried this in a clean WebApi project (and without EF5) instead of an MVC project and it worked as expected. Does that help identify where the problem might be?
Thanks
*UPDATE 2**
In the same MVC4 project I created an extension method for the Object class which is called ToJson. It uses Newtonsoft.Json.JsonSerializer to serialize my entities. Its this simple.
public static string ToJson(this object o, Dictionary<string, List<string>> IgnoreList)
{
JsonSerializer js = JsonSerializer.Create(new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Formatting.Indented,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
ContractResolver = new IgnoreListContractResolver(IgnoreList),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
js.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
var jw = new StringWriter();
js.Serialize(jw, o);
return jw.ToString();
}
And then in an MVC action i create a json string like this.
model.jsonUserList = db.Users.ToList().ToJson(IgnoreList);
Where the ignore list is created exactly like my previous post. Again I see the contract resolver run and correctly limit the properties list but the output json string still contains everything (including the properties I removed from the list). Does this help? I must be doing something wrong and now it seems like it isn't the MVC or web api framework. Could this have anything to do with EF interactions/ proxies /etc. Any ideas would be much appreciated.
Thanks
*UPDATE 3***
Process of elimination and a little more thorough debugging made me realize that EF 5 dynamic proxies were messing up my serialization and ContractResolver check for the type name match. So here is my updated IgnoreListContractResolver. At this point I am just looking for opinions on better ways or if I am doing something terrible. I know this is jumping through a lot of hoops just to use my EF objects directly instead of DTOs but in the end I am finding this solution is really flexible.
public class IgnoreListContractResolver : CamelCasePropertyNamesContractResolver
{
private readonly Dictionary<string, List<string>> IgnoreList;
public IgnoreListContractResolver(Dictionary<string, List<string>> i)
{
IgnoreList = i;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
List<JsonProperty> properties = base.CreateProperties(type, memberSerialization).ToList();
string typename = type.Name;
if(type.FullName.Contains("System.Data.Entity.DynamicProxies.")) {
typename = type.FullName.Replace("System.Data.Entity.DynamicProxies.", "");
typename = typename.Remove(typename.IndexOf('_'));
}
if (IgnoreList.ContainsKey(typename))
{
//remove anything in the ignore list and ignore case because we are using camel case for json
properties.RemoveAll(x => IgnoreList[typename].Contains(x.PropertyName, StringComparer.CurrentCultureIgnoreCase));
}
return properties;
}
}
I think it might help if you used Type instead of string for the ignore list's key type. So you can avoid naming issues (multiple types with the same name in different namespaces) and you can make use of inheritance. I'm not familiar with EF5 and the proxies, but I guess that the proxy classes derive from your entity classes. So you can check Type.IsAssignableFrom() instead of just checking whether typename is a key in the ignore list.
private readonly Dictionary<Type, List<string>> IgnoreList;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
List<JsonProperty> properties = base.CreateProperties(type, memberSerialization).ToList();
// look for the first dictionary entry whose key is a superclass of "type"
Type key = IgnoreList.Keys.FirstOrDefault(k => k.IsAssignableFrom(type));
if (key != null)
{
//remove anything in the ignore list and ignore case because we are using camel case for json
properties.RemoveAll(x => IgnoreList[key].Contains(x.PropertyName, StringComparer.CurrentCultureIgnoreCase));
}
return properties;
}
Then the ignore list must be created like this (I also used the short syntax for creating the list and dictionary):
var CompanyList = new List<string> {
"CompanyAccesses",
"ArchivedMemberships",
"AddCodes"
};
var IgnoreList = new Dictionary<Type, List<string>> {
// I just replaced "Company" with typeof(Company) here:
{ typeof(Company), CompanyList }
};
Be aware that, if you use my code above, adding typeof(object) as the first key to the ignore list will cause this entry to be matched every time, and none of your other entries will ever be used! This happens because a variable of type object is assignable from every other type.

Resources