How can I loosely reference modules in Prism so they can or cannot exist? - wpf

In this stackoverflow question I learned that Prism/Unity is not as decoupled as I thought, e.g. if I have this class which gets menuManager injected into its constructor, then I have to make sure that this class actually exists somewhere (I thought that you could just pull the .dll that contains the class and the container would deal with it, e.g. injecting a null in its place):
public class EmployeesPresenter
{
public EmployeesPresenter(IMenuManager menuManager)
{
}
}
But I can deal with that: the application cannot run without a MenuModule (or else as was suggested I could have a NullMenuModule which does nothing but keeps the application from breaking).
However, the application I am building will have a MenuManager class in the MenuModule and every module will have to register everything it wants to have in the menu with the MenuManager. However, I want to be able to swap out MenuModules e.g. have a InfragisticsMenuModule and have a TelerikMenuModule, etc.
However, when I am in e.g. the CustomersModule, in order to use TelerikMenuModule, I need to reference it. And when I want to use InfragisticsMenuModule, I need to reference that.
So how will I be able to "hot swap" TelerikMenuModule with InfragisticsMenuModule without recompiling all my modules with new references, e.g. I want to replace this:
Application.exe
Customers.dll
TelerikMenuModule.dll
with this:
Application.exe
Customers.dll
InfragisticsMenuModule.dll
and simply be able to restart the application and it runs with the new InfragisticsMenuModule.dll and does not complain that TelerikMenuModule.dll no longer exists.

This is where interfaces come in. You need something like:
public interface IMenuSystem
{
// whatever operations need to be offered by a menu system
}
Application.exe and Customers.dll may only refer to that interface. They aren't allow to know about a specific implementation.
Then you would use configuration steps (calling Register... methods or using a config file) to specify which type will provide the implementation of MenuSystem.

For obvious reason MEF comes to mind here and is designed for stuffs like this. I haven't had a chance to use Unity, so I'm not sure if it has something built in like this (i.e. scanning a directory for an IMenuModule implementation), but MEF can do this.
Suggestion also is to put this IMenuModule in a common assembly (separate from your other assembly). I usually called this thing Something.Core.dll.
So you might have: Application.exe, Customer.dll, Application.Core.dll, and your specific MenuModule implementation.
Your specific MenuModule implementation will reference the Application.Core assembly to gain access to its IMenuModule interface and implement it there.

Related

Deprecation of TableRegistry::get()

I'd like to ask what are your thought on deprecation of the TableRegistry::get() static call in CakePHP 3.6?
In my opinion it was not a good idea.
First of all, using LocatorAwareTrait is wrong on many levels. Most important, using traits in such way can break the Single Responsibility and Separation of Concerns principles. In addition some developers don't want to use traits as all because they thing that it breaks the object oriented design pattern. They prefer delegation.
I prefer to use delegation as well with combination of flyweight/singleton approach. I know that the delegation is encapsulated by the LocatorAwareTrait but the only problem is that it exposes the (get/set)TableLocator methods that can be used incorrectly.
In other words if i have following facade:
class Fruits {
use \Cake\ORM\Locator\LocatorAwareTrait;
public function getApples() { ... }
public function getOranges() { ... }
...
}
$fruits = new Fruits();
I don't want to be able to call $fruits->getTableLocator()->get('table') outside of the scope of Fruits.
The other thing you need to consider when you make such changes is the adaptation of the framework. Doing TableRegistry::getTableLocator()->get('table') every time i need to access the model is not the best thing if i have multiple modules in my application that move beyond simple layered architecture.
Having flyweight/singleton class like TableRegistry with property get to access desired model just makes the development more straight forward and life easier.
Ideally, i would just like to call TR::get('table'), although that breaks the Cake's coding standards. (I've created that wrapper for myself anyways to make my app bullet proof from any similar changes)
What are your thoughts?

Is it ok to create more modules in one assembly in prism?

I want to create two modules that describes toolbar and menu feature, but I don't want to define them in two different assemblies, and I tried to do it in that way,It works fine,but I'm afraid that would it takes twice as much as memory like just define them in one module? Follow is my demo code, they're written in one project.
public class MainMenuModule : IModule {
public void Initialize() {
RegionHelper.RegisterViewWithRegion(Shell.RegionNames.Menu, typeof(Views.Menu));
}
}
public class ToolBarModule : IModule {
public void Initialize() {
RegionHelper.RegisterViewWithRegion(Shell.RegionNames.ToolBarRegion, typeof(Views.ToolBar));
}
}
Note that RegionHelper is a wrapper prism region API.
That's fine. Although I don't really see a use case for two modules in one assembly...
And btw, the module definition classes are ready for collection once their Initialization methods return.
Is it OK to put two Prism modules in a single assembly? Well, yes. It will work and there is nothing stopping you from really. You can put as many module classes in a single assembly as you want to.
Keep in mind that a module is supposed to be a set of loosely coupled functional units though. If you put two modules in a single assembly, you can no longer load the first one without also loading the second one and vice versa. This may be a problem or it may not depending on how the modules are used by your application.
The possible down-sides of using too many assemblies are discussed here: Specific down-sides to many-'small'-assemblies?
This is generally not an issue.

Episerver - Why BlockData doesn't implement IContent

Does anybody knows why BlockData class doesn't directly implement IContent?
I know that during BlockData is being retrieve from database, proxy created by Castle implements IContent.
If StackOverflow isn't suitable place for this kind of a question, please move it.
Johan Björnfot at EPiServer explains some of the details in this post.
Excerpt:
"In previous versions of CMS was pages (PageData) the only content type that the content repository (traditionally DataFactory) handled. In CMS7 this has changed so now content repository (IContentRepository) handles IContent instances. This means that the requirement for a .NET type to be possible to save/load from content repository is that it implements the interface EPiServer.Core.IContent.
There are some implementations of IContent built into CMS like PageData and ContentFolder (used to group shared block instances) and it is also possible to register custom IContent implementations.If you look at BlockData though you will notice that it doesn’t implement IContent, how is then shared block instances handled?
The answer is that during runtime when a shared block instance is created (e.g. through a call to IContentRepository.GetDefault where T is a type inheriting from BlockData) the CMS will create a new .NET type inheriting T using a technic called mixin where the new generated subclass will implement some extra interfaces (including IContent)."
BlockData does implement IContent as it is intended to work both when added to another content item such as a PageData instance (a.k.a. Local Block), and as a standalone instance (a.k.a.Shared Block). In latter case the interface is added by using a mix-in though Castle Windsor so that it can be referenced.
The decision for this construct was based on wanting to be able to use the same rendering templates regardless if a block is local or shared. Therefor the choice stood between having a large number of empty properties on local blocks or the current solution using mixins. Both options were tested and mixins was selected as the preferred solution even though it's not a perfect one.
BlockData "does implement IContent", just do:
var myContent = (IContent)myBlock;
But, if you're by any chance handling a Block which itself is a property (not a ContentReference), that cast will throw an exception.
This will be true for 100% of all cases (... using Math.Round).

Ninject ActivationBlock as Unit of Work

I have a WPF application with MVVM. Assuming object composition from the ViewModel down looks as follows:
MainViewModel
OrderManager
OrderRepository
EFContext
AnotherRepository
EFContext
UserManager
UserRepository
EFContext
My original approach was to inject dependencies (from the ViewModelLocator) into my View Model using .InCallScope() on the EFContext and .InTransientScope() for everything else. This results in being able to perform a "business transaction" across multiple business layer objects (Managers) that eventually underneath shared the same Entity Framework Context. I would simply Commit() said context at the end for a Unit of Work type scenario.
This worked as intended until I realized that I don't want long living Entity Framework contexts at the View Model level, data integrity issues across multiple operations described HERE. I want to do something similar to my web projects where I use .InRequestScope() for my Entity Framework context. In my desktop application I will define a unit of work which will serve as a business transaction if you will, typically it will wrap everything within a button click or similar event/command. It seems that using Ninject's ActivationBlock can do this for me.
internal static class Global
{
public static ActivationBlock GetNinjectUoW()
{
//assume that NinjectSingleton is a static reference to the kernel configured with the necessary modules/bindings
return new ActivationBlock(NinjectSingleton.Instance.Kernel);
}
}
In my code I intend to use it as such:
//Inside a method that is raised by a WPF Button Command ...
using (ActivationBlock uow = Global.GetNinjectUoW())
{
OrderManager orderManager = uow.Get<OrderManager>();
UserManager userManager = uow.Get<UserManager>();
Order order = orderManager.GetById(1);
UserManager.AddOrder(order);
....
UserManager.SaveChanges();
}
Questions:
To me this seems to replicate the way I do business on the web, is there anything inherently wrong with this approach that I've missed?
Am I understanding correctly that all .Get<> calls using the activation block will produce "singletons" local to that block? What I mean is no matter how many times I ask for an OrderManager, it'll always give me the same one within the block. If OrderManager and UserManager compose the same repository underneath (say SpecialRepository), both will point to the same instance of the repository, and obviously all repositories underneath share the same instance of the Entity Framework context.
Both questions can be answered with yes:
Yes - this is service location which you shouldn't do
Yes you understand it correctly
A proper unit-of-work scope, implemented in Ninject.Extensions.UnitOfWork, solves this problem.
Setup:
_kernel.Bind<IService>().To<Service>().InUnitOfWorkScope();
Usage:
using(UnitOfWorkScope.Create()){
// resolves, async/await, manual TPL ops, etc
}

MyGroups not implemented in Communicator.UIAutomation

I'm working on a out of browser Silverlight app that provides some MS Office Communicator 2007 controls. I'm using the Automation SDK. The docs that were installed with the SDK state that there's a MyGroups property in the IMessenger2 interface, which will return the groups that a user has defined, but when I try to use it, I get a NotImplementedException. Here's the code that I'm using:
dynamic communicator = AutomationFactory.CreateObject("Communicator.UIAutomation");
communicator.AutoSignin();
foreach (dynamic g in communicator.MyGroups)
{
//Do something with the group
}
If I replace MyGroups with MyContacts, I can get the contact list just fine. Do I have to do something different to access properties in the IMessenger2 interface? I've seen a few things on the web that say that MyGroups was deprecated for Windows Messenger, but from the docs, it seems like it should be available for MS Office Communicator.
If I can't use MyGroups, is there another way to get the groups that a user has created?
The problem here is that the MyGroups property is marked as NotScriptable, meaning you can't call it in the way you are doing i.e. using the AutomationFactory. For security reasons, some properties and methods in the Automation API are not scriptable - this is to avoid malicious pages automating Communicator and carrying out certain tasks without you knowing.
It looks like the COM interop in Silverlight is treated in the same way as e.g. creating and calling the API from VBScript, so you won't be able to access any of the non-scriptable properties and methods. See the reference for details of which properties and methods are not scriptable.
I'm guessing this is going to seriously hobble your app. I think what's hurting you is the decision to go with Silverlight OOB. Is there any way you could use WPF (or even winforms) rather than Silverlight? If you did this, you could reference the API directly, and have full access to all properties/methods.
Otherwise, I can't think of too many options. You can't trap the OnContactAddedToGroup event, as this is not scriptable.
It might be possible to wrap the API with a .NET assembly, and expose it via COM, then instantiate it in the same way - but the Not Scriptable might still be respected in that case, so it won't buy you anything. Hard to say without trying it, and still a fairly horrible solution.
Edit: I've just given the wrapper method a try (needed to do something similar as a proof of concept for a customer), and it seems to work. This is the way I did it:
Create a new .NET class library. Define a COM interface:
[ComVisible(true)]
[Guid("8999F93E-52F6-4E29-BA64-0ADC22A1FB11")]
public interface IComm
{
string GetMyGroups();
}
Define a class that implements that interface (you'll need to reference CommunicatorAPI.dll from the SDK):
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[GuidAttribute("C5C5A1A8-9BFB-4CE5-B42C-4E6688F6840B")]
[ProgId("Test.Comm.1")]
public class Comm : IComm
{
public string GetMyGroups()
{
var comm = new CommunicatorAPI.MessengerClass();
var groups = comm.MyGroups as IMessengerGroups;
return string.Join(", ", groups.OfType<IMessengerGroup>().Select(g => g.Name).ToArray());
}
}
Build, and register using RegAsm. Then call from the OOB silverlight app:
dynamic communicator = AutomationFactory.CreateObject("Test.Comm.1");
MessageBox.Show(communicator.GetMyGroups());
Note, the same technique also works using the Lync API:
public string GetMyGroups()
{
var comm = LyncClient.GetClient();
return string.Join(", ", comm.ContactManager.Groups.Select(g => g.Name).ToArray());
}
Although this works, I can't really say whether it's a good practice, as it's working around a security restriction which was presumably there for a good reason. I guess the worst that could happen is that a malicious web page could potentially use the component, if it knew the ProgId of the control.
Edit: Also, using this method you'd need to be careful about memory leaks, e.g. make sure you're releasing COM objects when you're finished with them - easy enough to do, just needs a little discipline ;o)

Resources