DX Seattle's Release 1.2 Not resolving Class References

Issue #139 resolved
Larry Hengen created an issue

I am in the process of getting our XE4 based applications compiling with DX Seattle, and for some reason am experiencing EResolveExceptions on class types. Interfaces resolve as expected, and the code has not changed from that used with a previous version of Spring which no longer compilers under Seattle apparently due to TValue changes.

I tried to create a test project to demonstrate the issue by cutting down our registration units and I did get it to reproduce the error at one point. When I tried to remove all references to our library code I get a different behaviour. I get an Access Violation in the message loop when SetVisible() is called. It appears as if the type is resolved, but an instance of the type is not returned. If I comment out our splash form and substitute a TForm the same behaviour is present. In the previous version of Spring that we are using in production, the form is properly created.

Any assistance would be greatly appreciated as this is the last major hurdle in adopting DX Seattle.

Comments (7)

  1. Stefan Glienke repo owner

    Will look into that tomorrow.

    Not sure what version you are using in XE4 but Spring4D 1.1 supports Seattle - any previous version does not. Also 1.2 is not released yet (just saying so you be warned) and has quite some changes compared to 1.1 (including some breaking ones).

  2. Stefan Glienke repo owner

    The code you posted should never have worked (without some modification of the container or adding some extension) and here is why:

    You register a class without defining any service for it which will cause the container to do the inspection itself. If it finds interfaces (except IInterface and IInterfaceComponentReference) it registers them as service. If it does not find any, it registers the class itself as service - that is why you can resolve it as class when it does not implement your custom interfaces that you uncommented in TDynamicCustomVCLForm).

    However when looking for the correct constructor to call there is one quirk - it always finds the TObject constructor via RTTI even if some other constructor (like the one from TComponent) hides it and does not make it accessible by pure DI. That is why you are getting an AV in this code because it bypasses the TComponent.Create because it cannot inject the AOwner parameter. This can be solved by several ways:

    • adding a new parameterless constructor on your class calling inherited Create(nil);
    • adding a dummy TComponent or even the Application instance as TComponent to the container so it can actually resolve TComponent dependencies
    • extending the container by adding a subdependency resolver that specifically handles constructors with a certain pattern
  3. Stefan Glienke repo owner

    With the latest commit in release/1.2 I added a subdependency resolver that explicitly handles passing nil to the virtual TComponent constructor (or overrides in child classes) so it does not fall back to the TObject constructor.

  4. Larry Hengen reporter

    Thanks for looking at this so fast Stefan!

    We never modified the Spring code at all, but I cannot tell you exactly what version we were using although it was checked into our repo on 4/16/2013. The assumption that you want to use an interface as a service may be the usual usage scenario, but is a breaking change. Even with your latest commits to the release 1.2 branch I cannot run our current code since our TDynamicSplashform does implement several interfaces but we wish to register the class as a service.

    I am trying to modify our code to resolve this based on your initial reply. Thanks again for your efforts to resolve this and for open sourcing Spring4D etc.

  5. Stefan Glienke repo owner

    This is not a breaking change but as it has been like that forever. If you want to register a class to be resolvable as itself although it has resolvable interfaces you have to register it like this:

    RegisterType<TMyClass, TMyClass> or RegisterType<TMyClass>.Implements<TMyClass>.

    If you just call RegisterType<TMyClass> it does what I explained in my previous comment.

    Edit: Well from your commit date it seems you were using some very old version that had some half working mechanism to resolve classes although they were not properly registered which we removed.

    Extending or changing the resolve behavior can be modified using subdependency resolvers - you can see my latest commit how I used one to add the support for the TComponent constructor.

  6. Log in to comment