Trouble working with Services

Topics: CAB & Smart Client Software Factory
Jun 10, 2006 at 12:06 AM
originally posted by: KayLerch

Hello everybody,

I try to work with own services and got some exceptions. The module loader quick start was my orientation, in my opinion I did it like that, but it even doesn't work.

I have 3 projects : One for the Shell, one for a module, and one "Common"-project, which will distribute my service.

The service-interface in the Common :
public interface IUIExtensor
{
void extendShellMenu(UIConfigurationSection section, WorkItem workItem);
}

The implementation in the Common :
Service(typeof(IUIExtensor))
public class UIExtensor : IUIExtensor
{
public void extendShellMenu(UIConfigurationSection section, WorkItem workItem)
{
// Code
}
}

Loading the service in the module's view :
private IUIExtensor _extendService;

ServiceDependency
public IUIExtensor extendService
{
set { _extendService = value; }
}

Calling the service out of the view's constructor :
_extendService.extendShellMenu(section, _workItem);

The Exception :
Failed to load module from assembly Module_UserConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. Error was:
Failed to load module from assembly Module_UserConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. Error was:
Service Common.Services.IUIExtensor is not available in the current context.

Without the service-implementation everything works fine.
I need your help!

Thanks so far
Kay
Jun 10, 2006 at 1:44 AM
originally posted by: hmoeller

Did you add the Common assembly to the ProfileCatalog?

Generally speaking, you have to make sure that the all of your attributes are "seen" by the object builder some time during the buildup procedure. For modules, the most straight forward way to ensure this is by having them loaded by the module loader service. And missing to add a new module to the ProfileCatalog is one of my favourite mistakes... ;)
Jun 10, 2006 at 2:04 AM
originally posted by: KayLerch

okay, I added the Common Module to the Shell's ProfileCatalog. Now there is no exception anymore, but there is another problem :
when I call the service, the _extendService-attribute is null. I thought, when adding the View to the WorkItem, the attribute will be initialized :
workspace.Show(Items.AddNew<UserControl1>());
Jun 10, 2006 at 8:23 AM
originally posted by: ksunair

Kay,

I have the same problem. I did exactly like you did and I am getting null pointer exception. I even added the service in the view to see if that solves the problem and it doesn't. I would like to see the solution.
There are some people in this fourm very good at this and I think they may be on vaccation or watching world cup .. :)

Thanks.
Jun 10, 2006 at 10:10 AM
originally posted by: matiaswoloski

Short explanation

You get a null instance or a ServiceMissingException when the service is not found in the given scope.

Large explanation
Basically you have to think about the workitem hierarchy:

RootWorkItem
|
------------------------------------------------
| |
ModuleController1 ModuleController2
|
---------------------
| |
ChildWorkItem1 ChildWorkItem2

So, if you add a service to the RootWorkItem either by using the Service attribute or using the api (WorkItem.Services.AddNew) everyone will get that service.
If you add a service in the ModuleController2 (which is a workitem of type ControlledWorkItem<ModuleController1>), the workitem itself, ChildWorkItem1 and ChildWorkItem2 will be able to get the service. But ModuleContorller1 won't never see it and the RootWorkItem either.

So what happens when you use the Service attribute? There is an ObjectBuilder strategy that will go through your Module and will get all the types that has that attribute above and will register the service in the rootworkItem. That means that everybody will be able to see it.
BUT.... why you don't get it? Probably because there is a "load order" issue:
- you have two modules: Infrastructure.dll and Module1.dll
- Infrastructure module has some types decorated with Service
- Module1 use some of those services

If you load the Module1 first, then objectbuilder won't find the service because Infrastracture was not loaded yet!

The upcomming release of the Smart Client Software Factory will have more content regarding these topics

Thanks,
Matias
http://staff.southworks.net/blogs/matiaswoloski
Jun 10, 2006 at 10:30 AM
originally posted by: ksunair

Hi Matias,

Thanks for the info. Here is my question, I have added the addservice in the root workitem itself. I have my services declared and defined in Common.dll

Now, From one of the child work item, I am starting a new process thread in that thread I am trying to do the dependency injection for this service, do you think it is a right thing to do??

Again, like you said, I may be doing the order wrong.

Thanks for the late friday reply.
Jun 10, 2006 at 10:41 AM
originally posted by: matiaswoloski

Well probably there is a timing issue.
You should not launch your thread if the service is not in the container yet.

You can also use ondemand service retrieval using
WorkItem.Services.Get<IMyService>();
Compared to the ServiceDependency attribute, it won't happen during the initialization, you can ask for a service whenever you want.

Matias
Jun 10, 2006 at 11:06 AM
originally posted by: ksunair

When I tried
WorkItem.Services.Get

Services doesn't show up in the intellisense also I get an error
"An object reference is required for the nonstatic field, method, or property"


Sorry for being presistant since I have bunch of services I like to implement (to use EntLib features) I like to get a hang of services.

Thanks.
Jun 10, 2006 at 11:22 AM
originally posted by: matiaswoloski

You need the WorkItem to get the services. The WorkItem is the container of everything.

You can get the workitem injected in your class using this code:

ServiceDependency
public WorkIem WorkItem {
get { return _workItem; }
set { _workItem = value; }
}

Also, your class should be added to the WorkItem in some way. For example, if it is a view then you do this
WorkItem.Items.AddNew<Myview>()
The View is added to the WorkItem and then an ObjectBuilder strategy run through the views servicedependcy and inject the container workitem
It will also create the presenter and add it to the WorkItem when you use CreateNew

Matias
Jun 11, 2006 at 5:17 AM
originally posted by: Sankar74

Matias,

Thanks for the answer. But How can you add class to a workitem, when the it doesn't have a place in container like panel or something. Well, I can create one control and hide it but that is not a clean way.

So here is the situation, if you tell me what is the best way that would be great.

WorkItem
|
|-----------MyView This is added as a view to the workitem and it has a placeholder as well
|
|-------------------MyControl This is a control in the user control of MyView
|
|------------ MyClass This class is instansiated in my control button press

In this flow I want to use the service in MyClass. In here MyView is in its own dll and the services I am trying to access is in the Common.dl Anyhelp is appriciated.

Thanks again being patient and helping me.
Jun 13, 2006 at 12:38 AM
originally posted by: Sankar74

I try to add the Service to the RootWorkItem out of the ModuleInit from Common via Injection Constructor
InjectionConstructor
public CommonInit(ServiceDependency WorkItem rootWorkItem)
{
_rootWorkItem = rootWorkItem;
_rootWorkItem.Services.AddNew<IUIExtensor>();
}
So - that's the theory - the Service is added to the WorkItem, just after loading the Common-Module out of the ProfileCatalog.

So, what's the problem? It seems like the ModuleInit is never entered, even though I added the module to the Catalog :
<ModuleInfo AssemblyFile="Common.dll" />
<ModuleInfo AssemblyFile="Module_UserConfig.dll" />
When debugging, the Common-Module is never been passed (neither the injection constructor nor the overwirtten load-method)
The ModuleInit of the second module is entered correctly.

I am nearly desperated.

!!!!!!!!!!!
It's me KayLerch! I don't know, why I'm posting with Sankar74. Never logged in as him/her.
!!!!!!!!!!!
Jun 13, 2006 at 3:30 AM
originally posted by: Sankar74

Hi Kay,

I thought you never load common.dll through profile catalog. Is it not true? I don't have common.dll in my profile catalog and is everything else seems to be working.

When I checked Common.dll, it doesn't seem to have moduleinit either. Do we need to create them manually. If I add common.dll in the profile catalog the application doesn't start well, of course it doesn't have module init

Could you please enlighten me?

Thanks.

                                            • something is wrong, it is me ksunair not sanker... whats going on???? *************
Jun 13, 2006 at 4:07 AM
originally posted by: Sankar74

Kay,


I added module.cs to do the init as you mentioned. Here is my code and see if you find something wrong

using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.ObjectBuilder;
using MyProject.Common;
using MyProject.Common.Services;

namespace MyProject.Common
{
public class Module : ModuleInit
{
private WorkItem _rootWorkItem;

InjectionConstructor
public Module(ServiceDependency WorkItem rootWorkItem)
{
_rootWorkItem = rootWorkItem;
_rootWorkItem.Services.AddNew<MessageBoxService, IMessageBoxService>();
}

public override void Load()
{
base.Load();
}
}
}

With this I get an run time error and when I looked the event log, it errors out saying my IMessageService already exists. I just don't understand how can it be?

My profile catalog in shell.exe has common.dll as the first dll and still gives the error. Is there anything I am doing wrong?

Thanks.

                • This is ksunair not sanker ******************
Jun 13, 2006 at 4:48 AM
originally posted by: KayLerch

this is KayLerch (goddamn, what's up here?)

when creating the service in your implementation, you instanciate the service. when you want to create the instance on run time, you have to add the following attribute to the property in your service-implementation :

Service(typeof(MyService), AddOnDemand = true)

Now you can instanciate the service with AddNew<>.

------------------
But my problem is still alive. while debugging I found out, that :
- the ProfileCatalog.xml is found by the CAB
- all three entries are read (3 items in the modules-enum)
- all three assemblies are found

so why in hell it doesn't enter the load-method or the injected constructor of my moduleinit-class?
Nov 8, 2006 at 7:59 AM
originally posted by: techtickler

Hi there,
Just my two cents. Make sure the ProfileCatalog.xml is in the bin\debug where it can see the common.dll or whatever module dll you want to load.
Nov 8, 2006 at 8:02 AM
originally posted by: techtickler

Also, make sure the class access modifier is "public". What I found is that by default, it is private and this won't throw error when you compile the project and it will not throw Runtime error either. You will see the ModuleLoader service found the dll but not load them because of private class constructor.