It's been a while since I blogged. The reason why I try to blog again was this post in the support forum. Especially about how I layout a XAF-Module in a real world scenario.
The most important thing i care about is code. I like to be explicit about code and discoverable things. So i don't like designer files and too much reflection magic.
This will be an ongoing series of posts, so stay tuned.
Modules
No XAF-Application works without Modules
so I'll think i start there.
XAF uses reflection to figure out a lot about your project, thats really cool to get started, but on the other hand it comes at a cost. Performance and you need deep knowledge of the framework if something does not work as expected.
Basically there are a number questions we need ask ourself when we write a Module. What kind of module do we write? An editor Module (for example the HTML Property Editor Module) where there is only an editor? Or an fully fledged Module like the State Machine Module with BusinessObjects, Controllers, xafml, Editors, Services and all kind of code.
Normally i start with a module that starts something like this and add or remove code as i go along.
using System;
using System.Collections.Generic;
using System.Linq;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Updating;
using DevExpress.ExpressApp.Win.SystemModule;
namespace Scissors.ExpressApp.TokenEditor.Win
{
public class TokenEditorWindowsFormsModule : ModuleBase
{
public TokenEditorWindowsFormsModule()
=> DiffsStore = new NullDiffsStore(GetType().Assembly);
public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB)
=> ModuleUpdater.EmptyModuleUpdaters;
protected override IEnumerable<Type> GetDeclaredControllerTypes()
=> Type.EmptyTypes;
protected override IEnumerable<Type> GetDeclaredExportedTypes()
=> Type.EmptyTypes;
protected override IEnumerable<Type> GetRegularTypes()
=> Type.EmptyTypes;
protected override ModuleTypeList GetRequiredModuleTypesCore()
=> new ModuleTypeList(
typeof(SystemModule),
typeof(SystemWindowsFormsModule)
);
protected override void RegisterEditorDescriptors(EditorDescriptorsFactory editorDescriptorsFactory)
{
}
}
}
This removes all of the reflection magic that XAF is doing under cover. As you know, i basically write code almost only for Winforms, so i will focus on Winforms for now.
This module doe's absolutely nothing. There is no ApplicationModel
, no Controllers
, no ModelExtentions
.
As for dry code, i like this kind of module as a base class in a external assembly so i can reuse it later.
So let's to this.
using System;
using System.Collections.Generic;
using System.Linq;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Updating;
using Scissors.ExpressApp.Model.Core;
namespace Scissors.ExpressApp
{
public abstract class ScissorsBaseModule : ModuleBase
{
public ScissorsBaseModule()
=> DiffsStore = new NullDiffsStore(GetType().Assembly);
public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB)
=> ModuleUpdater.EmptyModuleUpdaters;
protected override IEnumerable<Type> GetDeclaredControllerTypes()
=> Type.EmptyTypes;
protected override IEnumerable<Type> GetDeclaredExportedTypes()
=> Type.EmptyTypes;
protected override IEnumerable<Type> GetRegularTypes()
=> Type.EmptyTypes;
protected override ModuleTypeList GetRequiredModuleTypesCore()
=> new ModuleTypeList(
typeof(SystemModule)
);
protected override void RegisterEditorDescriptors(EditorDescriptorsFactory editorDescriptorsFactory)
{
}
}
}
If you wondered what's inside the NullDiffsStore
class. It's simply a null implementation of the ModelStoreBase
class.
using System;
using System.Linq;
using System.Reflection;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Model.Core;
namespace Scissors.ExpressApp.Model.Core
{
public class NullDiffsStore : ModelStoreBase
{
Assembly _Assembly;
public NullDiffsStore(Assembly assembly)
=> _Assembly = assembly;
public override string Name => $"{nameof(NullDiffsStore)} of the assembly '{_Assembly.FullName}'";
public override void Load(ModelApplicationBase model)
{
}
public override bool ReadOnly => true;
}
}
So we need another base class for the Winforms stuff:
using System;
using System.Linq;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Win.SystemModule;
namespace Scissors.ExpressApp.Win
{
public abstract class ScissorsBaseModuleWin : ScissorsBaseModule
{
protected override ModuleTypeList GetRequiredModuleTypesCore()
=> base.GetRequiredModuleTypesCore()
.AndModuleTypes(typeof(SystemWindowsFormsModule));
}
}
As for the method AndModuleTypes
on the ModuleTypeList
: It's just a simple extension method to help keeping the code clean:
using System;
using System.Linq;
using DevExpress.ExpressApp;
namespace Scissors.ExpressApp
{
public static class ModuleBaseExtentions
{
public static ModuleTypeList AndModuleTypes(this ModuleTypeList moduleTypeList, params Type[] types)
{
moduleTypeList.AddRange(types);
return moduleTypeList;
}
}
}
So basically we are ready to consume our base classes to build a TokenEditorModule
.
using System;
using System.Linq;
using Scissors.ExpressApp.Win;
namespace Scissors.ExpressApp.TokenEditor.Win
{
public class TokenEditorWindowsFormsModule : ScissorsBaseModuleWin
{
}
}
That wraps it up for Modules so far, as i continue to develop more on the TokenEditorWindowsFormsModule
you'll starting to see more code.
The new csproj format
Thanks to the movement of .NET-Core we are getting a new csproj format thats much easier to handle (esp in source control) so lets have a look at the 3 Modules:
Scissors.ExpressApp
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DevExpress.ExpressApp" Version="17.2.3" />
</ItemGroup>
</Project>
Scissors.ExpressApp.Win
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DevExpress.ExpressApp.Win" Version="17.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Scissors.ExpressApp\Scissors.ExpressApp.csproj" />
</ItemGroup>
</Project>
Scissors.ExpressApp.TokenEditor.Win
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<!--<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>-->
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DevExpress.ExpressApp.Win" Version="17.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Scissors.ExpressApp.Win\Scissors.ExpressApp.Win.csproj" />
<ProjectReference Include="..\..\..\Scissors.ExpressApp\Scissors.ExpressApp.csproj" />
</ItemGroup>
</Project>
There is almost no code! As you might have spotted, at the time of writing this post DevExpress didn't provide nugets for XAF, so i build this little tool and used my last blog post how to use VSTS to host them
With the rise of netstandard2.0
i hope we'll able to see a shift of XAF to netstandard
as well.
There are some drawbacks using the new project format though. Currently there is no support for the ModelEditor
and designer support for WinForms
. But most of the time I don't care about that.
Comments
Alex Miller 11 Dec 2017 15:18
Thanks for sharing your methodology Manuel. This will come very handy when I tackle my todo item to split my huge base module into smaller more manageable modules. I look forward to the rest of the series.
Thank you
Your comment will appear in a few minutes.
Manuel Grundner 11 Dec 2017 15:56
@Alex stay tuned for the next Post, it is in the making!
Thank you
Your comment will appear in a few minutes.
Kirsten Greed 11 Mar 2018 00:05
Hi Manuel.
I am exploring your code and it looks really interesting. I am a bit confused though as I usually create my XAF,CodeFirst EF, solutions using the Wizard. I was not aware that XAF could work with .net core. Can you advise how to create the solution file?
Ah, I will check the next lesson.
Thank you
Your comment will appear in a few minutes.
Manuel Grundner 11 Mar 2018 06:51
Hi Kirsten!
XAF doesnt Support .net core yet, but xpo does. The other Thing is the new csproj. This works very Well, Just use the .netstandard Templates.
I try to make a Blogpost for further Clearance
:)
Thank you
Your comment will appear in a few minutes.
Kirsten Greed 11 Mar 2018 20:38
Thanks for the clarification :-) I will look forward to the post. Instructions on how to use the .netstandard Templates would be great!
Thank you
Your comment will appear in a few minutes.
Thank you
Your comment will appear in a few minutes.