BLOG by Dennis Garavsky: Webinar: What's Coming in XAF 13.2 (08/20/2013 )
BLOG by Apostolis Bekiaris: Flexible-fast layouts with CSS styles in the XAF application model
XAF has a build-in mechanism for customizing the layout’s. However nothing can be compared with the power of native CSS styles. In this post I will discuss eXpand’s implementation of CSS styles in the XAF application model.
How XAF renders a layout
To render a web layout XAF uses the WebLayoutManager class. This class queries all the IModelViewLayoutElements of a DetailView.Model.Layout and renders WebControls accordingly. For example for an IModelLayoutViewItem which is an interface that links the layout element with a property editor, the WebLayoutManager will render two controls. The first one will hold the caption/label and the second one will be the actual control depending on the type of the property editor. Take a look how an looks IModelLayoutViewItem in the Model Editor.
As you can see XAF supports a lot of attributes by default! Due to the large number of attributes WebLayoutManager introduces a table to hold and position our controls and a div above it to allow more flexibility in configuration.
It is also possible to use more more interfaces to design a layout. In the next image we see what is supported by default. However WebLayoutManager behavior is the same.
What we need to style?
Of course everything fast-flexible and without introducing complexities and XAF application model is the perfect tool for this. To summurize the previous paragraph we may wish to style
- The actual WebControl
- The label of the WebControl
- The container table
- The container div
- The whole view
How we get control instances
All XAF controls are rendered by property editors. For the web there is an abstract WebPropertyEditor (see a simplistic inheritance view).
It is enough to create a Controller then query all WebPropertyEditor ViewItems and subscribe to ControlCreated event. When the event raised we can do webPropertyEditor.Control and get the instance we need. Let’s see this a pseudo code snippet.
publicclassLayoutStyleController:ViewController<DetailView>,IModelExtender {
protectedoverridevoidOnActivated() {
base.OnActivated();
foreach (variteminView.GetItems<WebPropertyEditor>()) {
item.ControlCreated+=ItemOnControlCreated;
}
}
voidItemOnControlCreated(object sender, EventArgs eventArgs) {
var webPropertyEditor = ((WebPropertyEditor) sender);
webPropertyEditor.ControlCreated-=ItemOnControlCreated;
//here we have our webcontrol instance
var control = webPropertyEditor.Control;
}
How to get the layout container
WebLayoutManager is designed with the highest standards, so it is as easy as subscribing to its LayoutCreated event and getting the value of the Container property
protectedoverridevoidOnActivated() {
base.OnActivated();
View.LayoutManager.LayoutCreated+=LayoutManager_LayoutCreated;
}
voidLayoutManager_LayoutCreated(object sender, EventArgs e) {
View.LayoutManager.LayoutCreated-=LayoutManager_LayoutCreated;
//here we have our webcontrol instance
WebControl container = (WebControl) View.LayoutManager.Container;
}
How to get the rest of the objects we need to style
All the rest are only known from the WebLayoutManager which renders them using an advanced template mechanism. It is of course possible to override it however I want to continue working with pluggable controllers. So I will create an interface to help me parse those templates from a Controller.
publicinterfaceIWebLayoutManager {
eventEventHandler<TemplateInstantiatedEventArgs> Instantiated;
}
publicclassXpandLayoutManager : WebLayoutManager, IWebLayoutManager {
ViewItemsCollection_detailViewItems;
publiceventEventHandler<TemplateInstantiatedEventArgs> Instantiated;
protectedvirtualvoidOnInstantiated(TemplateInstantiatedEventArgs e) {
var handler = Instantiated;
if (handler != null) handler(this, e);
}
protectedoverrideLayoutBaseTemplateCreateLayoutItemTemplate() {
var layoutBaseTemplate = base.CreateLayoutItemTemplate();
layoutBaseTemplate.Instantiated+=LayoutBaseTemplateOnInstantiated;
return layoutBaseTemplate;
}
protectedoverrideLayoutBaseTemplateCreateLayoutGroupTemplate() {
var layoutBaseTemplate = base.CreateLayoutGroupTemplate();
layoutBaseTemplate.Instantiated+=LayoutBaseTemplateOnInstantiated;
return layoutBaseTemplate;
}
voidLayoutBaseTemplateOnInstantiated(object sender, TemplateInstantiatedEventArgs templateInstantiatedEventArgs) {
OnInstantiated(templateInstantiatedEventArgs);
}
Also I need to plug this custom XpandLayoutManager to my WebApplication descendant as below.
publicclassXpandWebApplication : WebApplication {
protectedoverrideLayoutManagerCreateLayoutManagerCore(bool simple) {
returnnewXpandLayoutManager();
}
Now it is possible to subscribe to to XpandLayoutManager Instanciated event from a controller and parse the templates to discover the label, the container table and the container div.
Extending the model
Having all the WebControls instances we want to style it is time to extend the model with a few interfaces so to control the styling from there. Bellow is the interface we need
publicinterfaceIModelLayoutStyle : IModelNode {
FontStyleFontStyle { get; set; }
ColorFontColor { get; set; }
ColorBackColor { get; set; }
stringCssClass { get; set; }
stringStyle { get; set; }
}
This interface can be used to extend IModelDetailView and IModelLayoutGroup to control the style of the whole view and of a grouped layout element as illustrated below.
publicclassLayoutStyleController:ViewController<DetailView>,IModelExtender {
publicvoidExtendModelInterfaces(ModelInterfaceExtenders extenders) {
extenders.Add<IModelDetailView, IModelLayoutStyle>();
extenders.Add<IModelLayoutGroup, IModelLayoutStyle>();
}
Now for the IModelLayoutViewItem as we discussed in the beginning there are three controls we need to style (actual, label and container). So we need to introduce another interface and to extend the IModelLayoutViewItem like the next one.
publicinterfaceIModelLayoutViewItemStyle {
ILayoutStyleLayoutStyle { get; }
}
publicinterfaceILayoutStyle:IModelNode {
IModelLayoutStyleContainer { get; }
IModelLayoutStyleControl { get; }
IModelLayoutStyleCaption { get; }
}
After extending the IModelLayoutViewItem the Model Editor now will display the next structure.
Finally it’s time to write the StyleProvider class and put them all in a reusable controller. However I will skip adding so much code in a post and I will redirect you to eXpandFramework GitHub repository (just grab the LayoutStyleController.cs and extend the WebLayoutManager as discussed above).
Demo
Bellow you can see a cool view a designed with CSS in the Application Model and the http://apobekiaris.blogspot.gr/2013/07/detailview-as-preview-in-gridview.html.
Always with zero code lines! and without fixed dimensions – totally relative!
Those of you that come often to eXpand forums will recognize for sure that this is a replicate of the Kunena forums (see for your self )!
This functionality is available with eXpandFramework 13.1.5.19.
Until next time,
Happy XAF’ing to all!
BLOG by Martynas Dauciunas: Whats this blog about ?
BLOG by Martynas Dauciunas: Co2 Emission calculator
- Portable (single executable)
- Doesn't require .Net framework 3.5 or SQL engine to be installed on the client machine, though both are being used by the software (which is fantastic for the developer and the customer)
- Can be run by lowest privilege user on a Windows PC (doesn't require elevated privileges)
- Lightweight (all the software with SQL and .Net 3.5 framework embedded inside the executable - weighs only ~60Mb)
- Rich and Intuitive User Experience (DevExpress components)
- MultiLanguage interface
- ORM (XPO and Entity Framework)
- Application Virtualization (XenoCode)
BLOG by Martynas Dauciunas: TP.Shell.XAF - ERP for manufacturing facilities
Few screenshots of the application:
BLOG by Martynas Dauciunas: MultiEdit component for DevExpress Express Application Framework (XAF)
and You want to update Fields like "Design pressure" for all selected objects.
Of course You can open each of them, and change them one by one. But :)
Select "MultiEdit" - that shows a Detail View for a objects selected, change the property You want, and press OK
It then Show's You what will be changed, and the progress of operation if very many objects are being updated.
BLOG by Martynas Dauciunas: Excel Importer for Express Application Framework (XAF)
So instead of writing a separate controller for each different excel file, o forcing people to use some predefined template, here what I came up with.
Wizard Page 1 lets You select Excell 2007 or newer file, select a Sheet that You want to import data from and see a PreView of the Data:
Next You can Map Excel columns to Your object Properties (or Let the Import Wizard try to Guess the mappings)
And See the progress of data import that is done in a separate thread.
BLOG by Martynas Dauciunas: Merge similar referenced objects controller for XAF
and there are no means of cleaning those dictionaries.
What do i mean by Dirty dictionaries ? here's a sample:
You have a list of whatever type of Objects, and You what to group them by one of the fields.
In this situation We're trying to group data object by property "Class". Here's what we get:
If we take a look at the "Dictionary", here's what we get:
Look's like that classe`es are identical... but.
What You'd what in such situation is somehow "Join" the 2 instances. An inexperienced user would simply delete one of them. But that doesn't solve the problem.
A clever SQL script here is not really possible. Why ?
Because it's after 5 years of working with SQL I have no idea how to write simple a script to find all objects using this dictionary value. It is possible of course. but.. I've found a better way :)
Here's how it works.
1. You go the the dictionary. Select object's that You want to Join and click Merge:
Then You Get a screen that Show's what will be merged, and asks You to select one that You want to stay:
Click OK, and The Controller collects (finds) ALL referencing objects that use one of the values being merged (Joined), and replaces them with the correct value.
BLOG by Martynas Dauciunas: TimeSpan Editor for XAF
using System;
using System.Text.RegularExpressions;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Win.Editors;
using DevExpress.XtraEditors.Mask;
using DevExpress.XtraEditors.Repository;
namespace Solution2.Module.Win
{
[PropertyEditor(typeof(TimeSpan))]
public class DurationPropertyEditor : DXPropertyEditor
{
public DurationPropertyEditor(Type objectType, IModelMemberViewItem model)
: base(objectType, model)
{
}
protected override object CreateControlCore()
{
return new StringEdit();
}
protected override void SetupRepositoryItem(RepositoryItem item)
{
base.SetupRepositoryItem(item);
((RepositoryItemStringEdit)item).Mask.MaskType = MaskType.RegEx;
((RepositoryItemStringEdit)item).Mask.EditMask
= @"\s*((\d?\d?\d?\s*(d(ays?)?)))?\s*((\d?\d?\s*(h(ours)?)?))?\s*(\d?\d?\s*(m(in(utes)?)?)?)?";
if (Control == null) return;
Control.ShowToolTips = true;
Control.ToolTip =
" Examples: " + Environment.NewLine +
" 1d = 1 Day" + Environment.NewLine +
" 1 day = 1 Day" + Environment.NewLine +
" 2d 5h 45 m = 2 Days 5 Hours 45 minutes" + Environment.NewLine +
" 2 days 4 hours 25 min = 2 Days 4 Hours 25 minutes" + Environment.NewLine
;
Control.EditValueChanged += Control_EditValueChanged;
}
void Control_EditValueChanged(object sender, EventArgs e)
{
WriteValue();
OnControlValueChanged();
}
protected override object GetControlValueCore()
{
return ParseTimeSpan(Control.Text);
}
protected override void ReadValueCore()
{
Control.EditValue = DecodeTimeSpan((TimeSpan) PropertyValue);
}
public static TimeSpan ParseTimeSpan(string s)
{
const string Quantity = "quantity";
const string Unit = "unit";
const string Days = @"(d(ays?)?)";
const string Hours = @"(h((ours?)|(rs?))?)";
const string Minutes = @"(m((inutes?)|(ins?))?)";
const string Seconds = @"(s((econds?)|(ecs?))?)";
var timeSpanRegex = new Regex(
string.Format(@"\s*(?<{0}>\d+)\s*(?<{1}>({2}|{3}|{4}|{5}|\Z))",
Quantity, Unit, Days, Hours, Minutes, Seconds),
RegexOptions.IgnoreCase);
var matches = timeSpanRegex.Matches(s);
var ts = new TimeSpan();
foreach (Match match in matches)
{
if (Regex.IsMatch(match.Groups[Unit].Value, @"\A" + Days))
{
ts = ts.Add(TimeSpan.FromDays(double.Parse(match.Groups[Quantity].Value)));
}
else if (Regex.IsMatch(match.Groups[Unit].Value, Hours))
{
ts = ts.Add(TimeSpan.FromHours(double.Parse(match.Groups[Quantity].Value)));
}
else if (Regex.IsMatch(match.Groups[Unit].Value, Minutes))
{
ts = ts.Add(TimeSpan.FromMinutes(double.Parse(match.Groups[Quantity].Value)));
}
else if (Regex.IsMatch(match.Groups[Unit].Value, Seconds))
{
ts = ts.Add(TimeSpan.FromSeconds(double.Parse(match.Groups[Quantity].Value)));
}
else
{
// Quantity given but no unit, default to Hours
ts = ts.Add(TimeSpan.FromHours(double.Parse(match.Groups[Quantity].Value)));
}
}
return ts;
}
public static string DecodeTimeSpan(TimeSpan timeSpan)
{
var time = string.Empty;
if (timeSpan.Days > 0)
time = timeSpan.Days + " Days";
if (timeSpan.Hours > 0)
time += (time != string.Empty ? " " : "") + timeSpan.Hours + " Hours";
if (timeSpan.Minutes > 0)
time += (time != string.Empty ? " " : "") + timeSpan.Minutes + " Minutes";
return time;
}
}
}
BLOG by Martynas Dauciunas: Excel Import Wizard Demo from eXpandFramework
BLOG by Martynas Dauciunas: How to Include eXpandFramework module into existing XAF application
This post describes how to add a feature from eXpandFramework into an existing XAF application.
1. Download the latest sources or binaries from here Make sure that DevExpress engine that You're using matches with the one that eXpand was built on | ![]() |
2. In Case You've decided to download Source's - You'll need to build them first. First make sure thet DX version matches (if not run the DX Project converter tool) Then run buildall32bit.cmd or buildall64bit.cmd depending on Your system. You'll find your build results in Xpand.DLL folder | ![]() |
3. Add necessary references to Your project. In this case : How to Add Excel import Wizard module to an Existing XAF application. | |
Add references, in Your main module, to:
| ![]() |
Add following code to Module.Designer.cs
//Add folowing code to WinModule.Designer.cs
this.RequiredModuleTypes.Add(typeof(Xpand.ExpressApp.ImportWiz.ImportWizModule))
//
//
this.RequiredModuleTypes.Add(typeof(ImportWizWinModule));
//
4. Run the application and use the New Module :)
Here's a blog post with video on How it Works
and a sample Solution that You can Download
BLOG by Manuel Grundner: Real MTier with DevExpress (Part 1)
Technologie overview
First of all one thing:
I like DevExpress and their controls, but i'm dissatisfied with the speed of the development on the mobile sector with large scaling XAF applications and the way they are managing their APIs. The options for developers are very restrictive in a manor of extensibility (thanks to VB.NET customers, i think. We develop C#. WE know what we shall or not shall do!). Why the fuck is every second method we like to use is fucking Browsable(false) or internal or is only reachable with real reflection pain? "For internal use only"? Are you fucking kidding me? DevExpress, please think of us developers when designing your APIs, not business analysts and script kiddies :(
Phew, that has done well. :)
Our main product is published to a wide range of clients, from small size scaling up to the enterprise sector.
One thing i really miss in our Portfolio are native apps for all kind of customers mobile devices. One real need for native apps is offline availability of the clients data (enterprise customers need to access their data anytime, regardless of connectionstate). So a Web solution will NOT meet our customers needs. Gladly we found Mono For Android and the MonoTouch framework from the Xamarin guys.
Technologie decision
But what data transport protocol should we use to support all upcomming platforms? WCF? SOAP? OData?
I think pure WCF is a overkill for most of our needs, and there is no tooling on all of our planned supported platforms (IOS for example).
SOAP has the problem that the effort to implement and extend a service for all our needs will take too long, and is a horror in maintainability across our customer needs. There is so much work on the meta-model before we get anything out/back from/to our database model.
DevExpress and OData
Then, all of a sudden, DevExpress announced support for OData in combination with XPO. Hurray! Getting the hands on the first versions and the result was sobering. :(
We have a huge XPO-Model (800+ PeristentClasses) with a legacy Database (migrating from our delphi application) and a legacy XPO-Model (many many many rookie mistakes was made in the beginning of migration).
Our Namespace model looks like something like this:
OurProduct.Model.Products
- Product.cs
- ProductCategory.cs
OurProduct.Model.Customer
- Customer.cs
- ContactInformation.cs
The problem here is there is no way to tell the XPO-Context-Driver to export complex types across different namespaces. That means we have to fetch up our data clientside with multiple queries to the OData-Service which is annoying and not very performant.
The second thing: The documentation was terrible. I don't know if there was a ability to filter out some of our classes from the ResourceSets.
So we decided to wait until DevExpress brings us new features with the next releases.
Starting with DXperience-12.1 they did a great job of cleaning the direct dependency of XAF on XPO. Nice stuff. And tadaaa there was the direct integration of OData into XPO!
And yes in this version the filtering of the ResourceSets is integrated! Also the Namespace is included in the Classname (sure not pretty, but hey it works!). Now we can start using this stuff and do some really cool things.
The Code
The code is pretty strate forward:
using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using DevExpress.Xpo.DB;
using DevExpress.Xpo;
using System.ServiceModel;
using DevExpress.Persistent.BaseImpl;
namespace MultitierSolution.OData
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MultitierSolutionODataService : XpoDataService
{
public MultitierSolutionODataService() : base(new MultitierSolutionContext("XpoContext", "MultitierSolutionModel", CreateDataLayer())) { }
static IDataLayer CreateDataLayer()
{
string conn = MySqlConnectionProvider.GetConnectionString("servername", "user", "password", "database");
DevExpress.Xpo.Metadata.XPDictionary dict = new DevExpress.Xpo.Metadata.ReflectionDictionary();
// Initialize the XPO dictionary.
dict.GetDataStoreSchema(typeof(Event).Assembly);
IDataStore store = XpoDefault.GetConnectionProvider(conn, DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);
return new ThreadSafeDataLayer(dict, store);
}
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.DataServiceBehavior.AcceptProjectionRequests = true;
}
}
public class MultitierSolutionContext : XpoContext
{
public MultitierSolutionContext(string containerName, string namespaceName, IDataLayer dataLayer)
: base(containerName, namespaceName, dataLayer) { }
public override bool HideMetaDataResourceProperty(Type classType, string propertyName)
{
if (classType == typeof(Event) && propertyName == "resourceIds")
return true;
return false;
}
public override bool HideMetaDataResourceSet(Type classType)
{
if (classType == typeof(Event))
return false;
return true;
}
}
}
Now whe have what we want. Only events are publised to the OData-Serivce:
<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?> <service xml:base="http://localhost/MultitierSolution.OData/ODataDemoService.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns="http://www.w3.org/2007/app"><workspace><atom:title>Default</atom:title><collection href="DevExpress_Persistent_BaseImpl_Event"><atom:title>DevExpress_Persistent_BaseImpl_Event</atom:title></collection></workspace></service>
And the result:
<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?><feed xml:base="http://localhost/MultitierSolution.OData/ODataDemoService.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom"><title type="text">DevExpress_Persistent_BaseImpl_Event</title><id>http://localhost/MultitierSolution.OData/ODataDemoService.svc/DevExpress_Persistent_BaseImpl_Event</id><updated>2012-07-21T22:58:09Z</updated><link rel="self" title="DevExpress_Persistent_BaseImpl_Event" href="DevExpress_Persistent_BaseImpl_Event" /><entry><id>http://localhost/MultitierSolution.OData/ODataDemoService.svc/DevExpress_Persistent_BaseImpl_Event(guid'61bf9c11-a05e-46fd-9a82-4eb91dabb1a2')</id><title type="text"></title><updated>2012-07-21T22:58:09Z</updated><author><name /></author><link rel="edit" title="DevExpress_Persistent_BaseImpl_Event" href="DevExpress_Persistent_BaseImpl_Event(guid'61bf9c11-a05e-46fd-9a82-4eb91dabb1a2')" /><link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/recurrencePattern" type="application/atom+xml;type=entry" title="recurrencePattern" href="DevExpress_Persistent_BaseImpl_Event(guid'61bf9c11-a05e-46fd-9a82-4eb91dabb1a2')/recurrencePattern" /><category term="MultitierSolutionModel.DevExpress_Persistent_BaseImpl_Event" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /><content type="application/xml"><m:properties><d:oid m:type="Edm.Guid">61bf9c11-a05e-46fd-9a82-4eb91dabb1a2</d:oid><d:Subject>test3</d:Subject><d:Description></d:Description><d:StartOn m:type="Edm.DateTime">2012-07-21T14:00:00</d:StartOn><d:EndOn m:type="Edm.DateTime">2012-07-21T15:30:00</d:EndOn><d:AllDay m:type="Edm.Boolean">false</d:AllDay><d:Location></d:Location><d:Label m:type="Edm.Int32">0</d:Label><d:Status m:type="Edm.Int32">2</d:Status><d:Type m:type="Edm.Int32">0</d:Type><d:RecurrenceInfoXml m:null="true" /></m:properties></content></entry><entry><id>http://localhost/MultitierSolution.OData/ODataDemoService.svc/DevExpress_Persistent_BaseImpl_Event(guid'af62e758-f181-4702-8712-76111fb4705f')</id><title type="text"></title><updated>2012-07-21T22:58:09Z</updated><author><name /></author><link rel="edit" title="DevExpress_Persistent_BaseImpl_Event" href="DevExpress_Persistent_BaseImpl_Event(guid'af62e758-f181-4702-8712-76111fb4705f')" /><link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/recurrencePattern" type="application/atom+xml;type=entry" title="recurrencePattern" href="DevExpress_Persistent_BaseImpl_Event(guid'af62e758-f181-4702-8712-76111fb4705f')/recurrencePattern" /><category term="MultitierSolutionModel.DevExpress_Persistent_BaseImpl_Event" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /><content type="application/xml"><m:properties><d:oid m:type="Edm.Guid">af62e758-f181-4702-8712-76111fb4705f</d:oid><d:Subject>some stuff</d:Subject><d:Description></d:Description><d:StartOn m:type="Edm.DateTime">2012-07-21T07:30:00</d:StartOn><d:EndOn m:type="Edm.DateTime">2012-07-21T12:30:00</d:EndOn><d:AllDay m:type="Edm.Boolean">false</d:AllDay><d:Location></d:Location><d:Label m:type="Edm.Int32">3</d:Label><d:Status m:type="Edm.Int32">2</d:Status><d:Type m:type="Edm.Int32">0</d:Type><d:RecurrenceInfoXml m:null="true" /></m:properties></content></entry><entry><id>http://localhost/MultitierSolution.OData/ODataDemoService.svc/DevExpress_Persistent_BaseImpl_Event(guid'e7d053b8-1970-4c0d-b6a3-c6c3dd3ca83a')</id><title type="text"></title><updated>2012-07-21T22:58:09Z</updated><author><name /></author><link rel="edit" title="DevExpress_Persistent_BaseImpl_Event" href="DevExpress_Persistent_BaseImpl_Event(guid'e7d053b8-1970-4c0d-b6a3-c6c3dd3ca83a')" /><link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/recurrencePattern" type="application/atom+xml;type=entry" title="recurrencePattern" href="DevExpress_Persistent_BaseImpl_Event(guid'e7d053b8-1970-4c0d-b6a3-c6c3dd3ca83a')/recurrencePattern" /><category term="MultitierSolutionModel.DevExpress_Persistent_BaseImpl_Event" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /><content type="application/xml"><m:properties><d:oid m:type="Edm.Guid">e7d053b8-1970-4c0d-b6a3-c6c3dd3ca83a</d:oid><d:Subject>test</d:Subject><d:Description></d:Description><d:StartOn m:type="Edm.DateTime">2012-07-21T01:00:00</d:StartOn><d:EndOn m:type="Edm.DateTime">2012-07-21T01:30:00</d:EndOn><d:AllDay m:type="Edm.Boolean">true</d:AllDay><d:Location></d:Location><d:Label m:type="Edm.Int32">0</d:Label><d:Status m:type="Edm.Int32">2</d:Status><d:Type m:type="Edm.Int32">0</d:Type><d:RecurrenceInfoXml m:null="true" /></m:properties></content></entry></feed>
WinRT Client Consumption
This task is like the use of a regular WCF-Service. Use the Add Service Reference
command in Visual Studio:
Rebuild. Booom! What the heck!? It's not compiling anymore. According to a bug in the code generation of VS12 in the xaml designers we cannot fix this error now :(
But you catch the idea, right?
Shame on you Microsoft! :>
Mono For Android Client Consumption
This task is a little bit trickier (for sure, its not MS technologie). But hey, we've got still tools to help us here.
I've found a blog post to create the client proxy on a Mono based platform.
Create a custom tool entry under Visual Studio Tools/External Tools...
to make this task a little bit more comfortable.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\DataSvcUtil.exe
/out:Client.cs /version:2.0 /uri:http://localhost/sampleservice/peoplefeed.svc
$(ProjectDir)
Run the command with our Argument
/out:Client.cs /version:2.0 /uri:http://localhost/MultitierSolution.OData/MultitierSolutionODataService.svc
Add the generated Client.cs
file to the Mono For android project and add a reference to System.Data.Services.Client
.
I don't know why this is no where documented. Neither on the Mono Documentation nor the Mono for Android documentation.
Further steps
In our next post we implement the android client to see some action!
This blog post is part of the real mtier with devexpress series
Demo Source
The source can be found at Bitbucket
BLOG by Manuel Grundner: FastSchemaProvider
I just released beta (hmm let me think..., okay V0.5) of FastSchemaProvider
Indoduction
We are having a huge defeat regarding upgrading consumer databases by XPO.
XPO isn't capable of extending column widths (varchars..), neither altering foreign keys, primary keys, defaults...
even XPObjectTypes (oh well, this should be possible trough orm, ...)
Goal
This project is intended to perform any schema transitions as fast as possible, taking account the underling database engine and the remote (provided) schema. This tool will to supersede any xpo (entity framework as will) change, regarding your database.
State
Its not yet ready for full productive use, but it will soon (for sql anywhere about Aug 15 2012).
Some parts are still missing:
- Altering columns taking place in indexes, would cause an error on most databases
- Same for foreign keys
- solutons/projects
- property string length definition => transfer them to staging schema
- provide general attribute for foreignkeys (sealed i don't care (╯°□°)╯︵ ┻━┻)
i'am at least sorry for bad english ^^
BLOG by Manuel Grundner: FastSchemaProvider V0.6
I've just updated FastSchemaProvider
Next internal steps was to attach a XPO/XAF-Application (internal needs ;-)
So I was refactoring the DBChanger => DVDiff, and sic adding otpions to compare...
Behaves well (but slowly, anybody an speed up option)?
next update in a few days
BlOG by Mike Calvert: .NET Applications on Linux
BLOG by Marcelo Paiva: DevExpress XAF – Introdução
Constantemente encontro com amigos que desejam criar um produto rapidamente e logo sair ganhando dinheiro. Muitos deles querem implementar algo para atender pontualmente certo mercado ou necessidade das pessoas, outros mais enfáticos planejam criar um “facebook 2″.
Sei que muitos não falam mas quando pensam também em qual ferramenta irá utilizar para criar o “inovador” produto para ficar “rico”, querem algo que lhe traga produtividade, até porque é preciso construir rapidamente antes que outro em algum lugar no mundo crie algo parecido.
Vou então fazer hoje uma sugestão de ferramenta e ainda fazer uma pequena introdução sobre ela, no entanto vou dizendo logo que é uma ferramenta paga, sei que praticamente todo mundo na área de desenvolvimento sai da faculdade querendo ter acesso a todas ferramentas gratuitamente, melhor dizendo, pensando assim: “quero usar somente produtos e ferramentas gratuitas e todo que eu fizer com elas vou cobrar”. Não vou entrar muito no mérito da questão pois seria outro post.
O que é bom destacar aqui é o fato de que você precisa antes de empreender qualquer negócio, pode ser ele uma loja de roupas e também a criação de um software, avaliar o investimento necessário é de fundamental importância. Assim como ao abrir uma loja você deverá investir, este mesmo raciocínio serve no momento de criar um produto.
Neste cenário sei que muitos procuram alguma ferramenta de geração de código/produto, eu particularmente no meu histórico profissional já me deparei com algumas como: Genexus (http://www.genexus.com/global/inicio?pt), GAS (http://www.gasweb.com.br), Maker (http://www.softwell.com.br). No entanto hoje estou usando em alguns projetos o XAF (http://www.devexpress.com/Products/NET/Application_Framework/) da Devexpress.
Sei perfeitamente que o XAF não atende em alguns casos e também pode não ser o melhor para algumas pessoas, mas tenho certeza que está atendendo nossa empresa em projetos importantes e trazendo uma produtividade sem igual.
Antes de começar a mostrar um pouco sobre ele, você pode baixar uma versão Trial no link: http://www.devexpress.com/Home/Try.xml
Vamos ao nosso “inovador” cadastro de pessoas usando o XAF. Estamos utilizando o Visual Studio 2012 e após instalar o produto são inseridos vários templates de projetos, dentre eles um grupo do eXpressApp Framework (XAF) que apresenta a tela abaixo (clique para ampliar).
Criaremos um projeto simples chamado “CadastroPessoas” a partir do template “Cross-Plataform Application” que permite que nossa aplicação tenha uma interface Windows e Web ao mesmo tempo.
Veja como fica a solução no VS2012, podemos perceber que o XAF trabalha de forma modular no entanto não se preocupe com os detalhes agora.
A ideia da ferramenta é que você se preocupe com as regras de negócio e ela cuida dos demais detalhes, então vamos logo criar uma definição de objeto de negócio chamada “Pessoa”. Vamos fazer isso no projeto “CadastroPessoas.Module” na pasta “BusinessObjects” de acordo com a tela abaixo.
Iremos incluir algumas propriedades básicas como Nome, Endereço e Telefone para nossa Classe de negócio de acordo com a tela a seguir.
Com isso você já pode executar sua aplicação e poderá fazer o cadastramento das pessoas.
Bom aí você poderá me perguntar que “mágica” é essa? Onde está sendo persistido os objetos ??
Sei que já temos até aqui algumas perguntas interessantes, então vamos apresentando algumas considerações sobre o “jeito” de pensar do XAF.
1 – Por default o XAF irá usar o SQL Express com sua instância padrão e vai criar automaticamente um banco de dados com o nome da solução.
2 – Ele cria uma interface padrão de navegação usando (é claro) os componentes do DevExpress que pode ser customizado.
3 – Como ele trabalha de forma modular, neste momento são incluídos somente os módulos básicos. No entanto podemos incluir validações, controle de aparência, relatórios e gráficos.
Vamos criar outros posts cada módulo separadamente incrementando nosso projeto “inovador”.
Espero que você possa entender mais sobre o XAF e considerar o uso desta poderosa ferramenta.
Até mais!
BLOG by Marcelo Paiva: DevExpress XAF – Validação
Olá pessoal, vamos continuar nossa séria sobre o XAF.
Se não leu ainda, veja nosso artigo anterior para melhor entender o que estamos falando no link: XAF – Introdução
Neste artigo vamos tratar do assunto validação, como nós criamos um projeto bem simples no artigo anterior que é um cadastro de pessoas e neste caso precisaremos de indicar campos obrigatórios como por exemplo o ‘Nome’ da pessoa.
Vamos ver como funciona isso no XAF!?
Em cada módulo do XAF existe um arquivo ‘Module.cs’ (isto no projeto base .Module, nos demais há uma sigla anterior ao nome) que é usado para fazer a configuração do módulo em questão, aqui então já vale entender porque em nosso exemplo foi criada 1 (uma) solução com 5 (cinco) projetos, no gráfico abaixo explicamos os relacionamentos entre eles e o que é cada um.
- Projeto.Module = Contém a implementação as entidades de negócio e suas regras gerais
- Projeto.Module.Web = Mantêm a implementação das regras específicas para o projeto Web
- Projeto.Module.Win = Mantêm a implementação das regras específicas para o projeto Windows
- Projeto.Web = Projeto final em Asp.Net
- Projeto.Win = Projeto final em WindowsForms
Voltando a questão, você deve abrir o arquivo ‘Module.cs’ no projeto base .Module, e através da toolbox incluir o módulo ‘ValitadionModule’ conforme a imagem a seguir:
Com este módulo adicionado podemos então criar nossas regras de validação. Isso pode ser feito de duas maneiras, através do código e também com o editor de modelo (no arquivo Model.xafml), no entanto apresentar aqui como fazer através do código que a meu ver fica mais organizado.
Então ao expandir o arquivo Pessoa.bo temos o arquivo ‘Pessoa.bo.designer.cs’ com a especificação dos atributos da classe e precisaremos adicionar então a validação. Veja abaixo o que precisamos fazer no código para incluir o que precisamos.
Após implementar os passos anteriores e executar sua aplicação já teremos a regra de validação aplicada para os módulos Web e Windows. Veja abaixo como fica a interface validando o atributo ‘Nome’ na aplicação.
Isso é só um pequeno exemplo do que é possível fazer com este módulo de validação. É bastante simples escrever uma regra e indicar onde e quando ela deve ser aplicada. Também podemos colocar controle de aparência condicionado a um valor de atributo qualquer, iremos ver isso no próximo artigo.
Até a próxima.
BlOG by Mike Calvert: How to use the XPCursor
BLOG by Robert Anderson: Installing Ruby with Homebrew and rbenv on Mac OS X Mountain Lion
Install Ruby with rbenv
I decided to setup Octopress on my Mac so that I can publish blog posts from either Windows or MacOS. I’m on OS X 10.8.2.
I tried to follow the Octopress instructions for installing Ruby but ran into a few problems.
Install Homebrew
Homebrew is a package manager for OS X. Open Terminal and install Homebrew with:
$ ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
Once the installation is successful, you can run the following command to check your environment.
$ brew doctor
Apparently, you should see:
Your system is raring to brew
But instead I had 3 warnings.
Warning: /usr/bin occurs before /usr/local/bin
This means that system-provided programs will be used instead of those
provided by Homebrew. The following tools exist at both paths:
gcov-4.2
Consider amending your PATH so that /usr/local/bin
occurs before /usr/bin in your PATH.
This one can be fixed by modifying your .profile
file. Create it if it doesn’t already exist. Use nano ~/.profile
if you don’t have a preferred editor.
123 |
|
Google tells me the other two warnings are related to Mono being installed and can be ignored.
Warning: /Library/Frameworks/Mono.framework detected This can be picked up by CMake's build system and likely cause the build to fail. You may need to move this file out of the way to compile CMake.Warning: You have a non-Homebrew 'pkg-config' in your PATH: /usr/bin/pkg-config => /Library/Frameworks/Mono.framework/Versions/2.10.9/bin/pkg-config This was most likely created by the Mono installer. `./configure` may have problems finding brew-installed packages using this other pkg-config.
Ploughing on…
Install rbenv
Rbenv handles the installation of multiple Ruby environments.
$ brew update
$ brew install rbenv
$ brew install ruby-build
Install gcc
If I try to install Ruby immediately, I get
ERROR: This package must be compiled with GCC, but ruby-build
couldn't find a suitable `gcc` executable on your system.
Please install GCC and try again.
...
XCode used to ship with a compatible gcc, but no longer does. We can install it with Homebrew.
$ brew update
$ brew tap homebrew/dupes
$ brew install autoconf automake apple-gcc42
The new gcc will coexist happily alongside the default one.
Install Ruby 1.9.3
Now we can install Ruby.
$ rbenv install 1.9.3-p194
$ rbenv rehash
Next run
$ ruby --version
Hmm… I get
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]
Shouldn’t it say ruby 1.9.3? It turns out you need to add the following to the end of your .profile
.
12 |
|
Now quit and restart Terminal.
$ rbenv global 1.9.3-p194
$ ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.2.1]
Ruby 1.9.3 is installed correctly. If I quit and restart Terminal, ruby --version
is still 1.9.3.
BLOG by Manuel Grundner: How to use Dependency Injection in XAF
Dependency injection
XAF has the ability to provide DI over Domain-Componants, we use XPO so we don't have a chance to use this feature (and yes, i hate static methods! (testing testing testing))
http://en.wikipedia.org/wiki/Dependency_injection
Why?
It's simple. We have a legacy model with a lot of customers, and can't affort to recreate the model all and all over.
Testing abilities are also a huge factor for our development.
How?
It was a really tricky task to tell XAF & XPO the trick of DI (or IOC http://en.wikipedia.org/wiki/Inversionofcontrol)
Okay Let's start
First of all: The sample uses Unity (http://unity.codeplex.com) cause it's well known and supported from microsoft (and fits our needs perfect), but it's also possible to extract this hard dependency through the Service Locator Pattern if you like to. (http://en.wikipedia.org/wiki/Servicelocatorpattern)
The key interfaces!
First we need two simple interface's we can talk to:
public interface IUnityContainerProvider
{
IUnityContainer UnityContainer { get; set; }
}
public interface IUnityModule
{
void InitUnityContainer(IUnityContainer unityContainer);
void UnityContainerInitialized(IUnityContainer unityContainer);
}
The IUnityContainerProvider
is used for any class resolved by the UnityContainer
to inject himself (we prefer PropertyInjection
cause of the Session constructor forced by XPO.
The IUnityModule is intended to be implemented by a DevExpress.ExpressApp.ModuleBase
derived type.
The InitUnityContainer
is inteded to be called after the Application.Setup()
method for each module loaded. The UnityContainerInitialized
is called after the InitUnityContainer
for each module. So we can override behavior provided by other modules.
How the hell can this work with XAF?
I've played with this really long and still facing out some problems i've seen with our implementation, but this works for almost 2 years now. So i can say it works almost with no problems so long. Till the next XAF update ;)
Children
Unity has the ability to create ChildContainer's from parent containers. This is nice cause the Frame concept in XAF is almost the same for the view handling.
We could reuse this for the domain-logic so we can simply write domain code without having to deal with different Sessions/UnitOfWorks/ObjectSpaces.
Code it please!
Okay okay, dont hustle...
UnityUnitOfWork
First of all we need a UnityUnitOfWork
. This Class provides a UnityContainer and stores itself as a instance of type Session
and UnitOfWork
.
public class UnityUnitOfWork : UnitOfWork, IUnityContainerProvider
{
public UnityUnitOfWork() { }
public UnityUnitOfWork(DevExpress.Xpo.Metadata.XPDictionary dictionary) : base(dictionary) { }
public UnityUnitOfWork(IDataLayer layer, params IDisposable[] disposeOnDisconnect) : base(layer, disposeOnDisconnect) { }
public UnityUnitOfWork(IObjectLayer layer, params IDisposable[] disposeOnDisconnect) : base(layer, disposeOnDisconnect) { }
private IUnityContainer _UnityContainer;
public IUnityContainer UnityContainer
{
get
{
return _UnityContainer;
}
set
{
value.RegisterInstance<UnitOfWork>(this, new HierarchicalLifetimeManager());
value.RegisterInstance<Session>(this, new HierarchicalLifetimeManager());
_UnityContainer = value;
}
}
protected override NestedUnitOfWork CreateNestedUnitOfWork()
{
return new NestedUnityUnitOfWork(this);
}
}
NestedUnityUnitOfWork
Cause XPO supports nested transactions we shouldn't miss the NestedUnitOfWork
who is also a fullUnityOfWork
.
public class NestedUnityUnitOfWork : NestedUnitOfWork, IUnityContainerProvider
{
protected internal NestedUnityUnitOfWork(Session parent)
: base(parent)
{
UnityContainer = (parent as IUnityContainerProvider).UnityContainer.CreateChildContainer();
UnityContainer.RegisterInstance<NestedUnitOfWork>(this, new HierarchicalLifetimeManager());
UnityContainer.RegisterInstance<UnitOfWork>(this, new HierarchicalLifetimeManager());
UnityContainer.RegisterInstance<Session>(this, new HierarchicalLifetimeManager());
}
public IUnityContainer UnityContainer { get; set; }
protected override NestedUnitOfWork CreateNestedUnitOfWork()
{
return new NestedUnityUnitOfWork(this);
}
}
But what about XAF?
We need to provide the same functionality to the XPObjectSpace
as well to the XPNestedObjectSpace
.
ObjectSpaces
UnityObjectSpace
public class UnityObjectSpace : XPObjectSpace, IUnityContainerProvider, IUnityObjectSpace
{
public UnityObjectSpace(UnitOfWork unitOfWork) : base(unitOfWork) { }
public UnityObjectSpace(ITypesInfo typesInfo, XpoTypeInfoSource xpoTypeInfoSource, CreateUnitOfWorkHandler createUnitOfWorkDelegate) : base(typesInfo, xpoTypeInfoSource, createUnitOfWorkDelegate) { }
public IUnityContainer UnityContainer
{
get
{
if (Session is UnityUnitOfWork)
return (Session as UnityUnitOfWork).UnityContainer;
return null;
}
set { }
}
protected override UnitOfWork RecreateUnitOfWork()
{
var uow = base.RecreateUnitOfWork();
if (uow is UnityUnitOfWork)
(uow as UnityUnitOfWork).UnityContainer.RegisterInstance<IObjectSpace>(this, new HierarchicalLifetimeManager());
return uow;
}
public override IObjectSpace CreateNestedObjectSpace()
{
var os = new UnityNestedObjectSpace(this);
(os.Session as IUnityContainerProvider).UnityContainer.RegisterInstance<IObjectSpace>(os, new HierarchicalLifetimeManager());
return os;
}
}
UnityNestedObjectSpace
public class UnityNestedObjectSpace : XPNestedObjectSpace, IUnityContainerProvider
{
public UnityNestedObjectSpace(IObjectSpace parentObjectSpace)
: base(parentObjectSpace) {}
public IUnityContainer UnityContainer
{
get
{
return (Session as IUnityContainerProvider).UnityContainer;
}
set {}
}
public override IObjectSpace CreateNestedObjectSpace()
{
var nestedOS = new UnityNestedObjectSpace(this);
nestedOS.AsyncServerModeSourceResolveSession = AsyncServerModeSourceResolveSession;
nestedOS.AsyncServerModeSourceDismissSession = AsyncServerModeSourceDismissSession;
(nestedOS.Session as IUnityContainerProvider).UnityContainer.RegisterInstance<IObjectSpace>(nestedOS, new HierarchicalLifetimeManager());
return nestedOS;
}
protected override UnitOfWork RecreateUnitOfWork()
{
var Result = base.RecreateUnitOfWork();
(Result as IUnityContainerProvider).UnityContainer.RegisterInstance<IObjectSpace>(this, new HierarchicalLifetimeManager());
return Result;
}
}
Okay we have almost all we need, hurry up!
There are only 2 things missing. The infrastrucure for the ObjectSpaceProviders
and the XAFApplication
.
ObjectSpaceProviders & Application
There are 2 versions of the ObjectSpaceProvider
: Secured and Unsecured.
First the unsecured version:
public class UnityObjectSpaceProvider : XPObjectSpaceProvider, IUnityContainerProvider
{
public IUnityContainer UnityContainer { get; set; }
public UnityObjectSpaceProvider(string connectionString, IDbConnection connection, IUnityContainer unityContainer) : base(connectionString, connection)
{
UnityContainer = unityContainer;
unityContainer.RegisterInstance(typeof(IObjectSpaceProvider), this, new ContainerControlledLifetimeManager());
}
public UnityObjectSpaceProvider(IXpoDataStoreProvider dataStoreProvider, IUnityContainer unityContainer)
: base(dataStoreProvider)
{
UnityContainer = unityContainer;
unityContainer.RegisterInstance(typeof(IObjectSpaceProvider), this, new ContainerControlledLifetimeManager());
}
public UnityObjectSpaceProvider(IXpoDataStoreProvider dataStoreProvider, ITypesInfo typesInfo, XpoTypeInfoSource xpoTypeInfoSource, IUnityContainer unityContainer)
: base(dataStoreProvider, typesInfo, xpoTypeInfoSource)
{
UnityContainer = unityContainer;
unityContainer.RegisterInstance(typeof(IObjectSpaceProvider), this, new ContainerControlledLifetimeManager());
}
protected override IDataLayer CreateDataLayer(IDataStore dataStore)
{
var dataLayer = new SimpleDataLayer(this.XPDictionary, dataStore);
return dataLayer;
}
protected override IObjectSpace CreateObjectSpaceCore()
{
var os = new UnityObjectSpace(TypesInfo, XpoTypeInfoSource, CreateUnitOfWorkDelegate);
os.UnityContainer.RegisterInstance<IObjectSpace>(os, new HierarchicalLifetimeManager());
return os;
}
protected override UnitOfWork CreateUnitOfWork(IDataLayer dataLayer)
{
var uow = new UnityUnitOfWork(dataLayer, null)
{
UnityContainer = UnityContainer.CreateChildContainer()
};
return uow;
}
}
And Secured:
public class SecureUnityObjectSpaceProvider : XPObjectSpaceProvider, IUnityContainerProvider
{
private ISelectDataSecurityProvider SelectDataSecurityProvider;
public bool AllowICommandChannelDoWithSecurityContext { get; set; }
public SecureUnityObjectSpaceProvider(ISelectDataSecurityProvider selectDataSecurityProvider, IXpoDataStoreProvider dataStoreProvider, ITypesInfo typesInfo, XpoTypeInfoSource xpoTypeInfoSource, IUnityContainer unityContainer)
: base(dataStoreProvider, typesInfo, xpoTypeInfoSource)
{
UnityContainer = unityContainer;
SelectDataSecurityProvider = selectDataSecurityProvider;
AllowICommandChannelDoWithSecurityContext = true;
}
public SecureUnityObjectSpaceProvider(ISelectDataSecurityProvider selectDataSecurityProvider, IXpoDataStoreProvider dataStoreProvider, IUnityContainer unityContainer)
: base(dataStoreProvider)
{
UnityContainer = unityContainer;
SelectDataSecurityProvider = selectDataSecurityProvider;
AllowICommandChannelDoWithSecurityContext = true;
}
public SecureUnityObjectSpaceProvider(ISelectDataSecurityProvider selectDataSecurityProvider, string databaseConnectionString, IDbConnection connection, IUnityContainer unityContainer)
: base(databaseConnectionString, connection)
{
UnityContainer = unityContainer;
SelectDataSecurityProvider = selectDataSecurityProvider;
AllowICommandChannelDoWithSecurityContext = true;
}
public IUnityContainer UnityContainer { get; set; }
protected override IDataLayer CreateDataLayer(IDataStore dataStore)
{
var datalayer = new SimpleDataLayer(dataStore);
return datalayer;
}
protected override IObjectSpace CreateObjectSpaceCore()
{
var os = new UnityObjectSpace(TypesInfo, XpoTypeInfoSource, CreateUnitOfWorkDelegate);
os.UnityContainer.RegisterInstance<IObjectSpace>(os, new HierarchicalLifetimeManager());
return os;
}
protected override UnitOfWork CreateUnitOfWork(IDataLayer dataLayer)
{
UnityUnitOfWork uow = new UnityUnitOfWork(dataLayer, null);
uow.UnityContainer = UnityContainer.CreateChildContainer();
SessionObjectLayer currentObjectLayer = new SecuredSessionObjectLayer(AllowICommandChannelDoWithSecurityContext, uow, true, null, new SecurityRuleProvider(XPDictionary, SelectDataSecurityProvider.CreateSelectDataSecurity()), null);
var secureUnitOfWork = new UnityUnitOfWork(currentObjectLayer, uow);
secureUnitOfWork.UnityContainer = uow.UnityContainer;
return secureUnitOfWork;
}
}
Note: The second one is almost a clone of the SecuredObjectSpaceProvider
provided by DevExpress but we didn't want to intercept this class with reflection so we made a clone to inject our needs.
Application & Bootstrapping
public class UnityModuleInitializer
{
public void InitUnityModules(IUnityContainer container, IEnumerable<IUnityModule> modules)
{
foreach (var module in modules)
module.InitUnityContainer(container);
foreach (var module in modules)
module.UnityContainerInitialized(container);
}
}
public class UnityWinApplication : WinApplication, IUnityContainerProvider
{
public IUnityContainer UnityContainer { get; set; }
public UnityWinApplication() : this(new UnityContainer()) { }
public UnityWinApplication(IUnityContainer container)
{
UnityContainer = container;
UnityContainer.RegisterInstance<XafApplication>(this, new ContainerControlledLifetimeManager());
SettingUp += ParaXAFApplication_SettingUp;
}
protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args)
{
args.ObjectSpaceProvider = CreateUnityObjectSpaceProvider(args);
}
public XPObjectSpaceProvider CreateUnityObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs e)
{
return new UnityObjectSpaceProvider(e.ConnectionString, e.Connection, UnityContainer);
}
void ParaXAFApplication_SettingUp(object sender, SetupEventArgs e)
{
new UnityModuleInitializer().InitUnityModules(UnityContainer, Modules.OfType<IUnityModule>());
}
}
Bring the stuff together
The Application:
public partial class XAFDISolutionWindowsFormsApplication : UnityWinApplication
{
public XAFDISolutionWindowsFormsApplication(IUnityContainer container)
: base(container)
{
InitializeComponent();
DelayedViewItemsInitialization = true;
}
public XAFDISolutionWindowsFormsApplication() : this(new UnityContainer()) { }
private void XAFDISolutionWindowsFormsApplication_DatabaseVersionMismatch(object sender, DevExpress.ExpressApp.DatabaseVersionMismatchEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
e.Updater.Update();
e.Handled = true;
}
else
{
throw new InvalidOperationException(
"The application cannot connect to the specified database, because the latter doesn't exist or its version is older than that of the application.\r\n" +
"This error occurred because the automatic database update was disabled when the application was started without debugging.\r\n" +
"To avoid this error, you should either start the application under Visual Studio in debug mode, or modify the " +
"source code of the 'DatabaseVersionMismatch' event handler to enable automatic database update, " +
"or manually create a database using the 'DBUpdater' tool.\r\n" +
"Anyway, refer to the 'Update Application and Database Versions' help topic at http://www.devexpress.com/Help/?document=ExpressApp/CustomDocument2795.htm " +
"for more detailed information. If this doesn't help, please contact our Support Team at http://www.devexpress.com/Support/Center/");
}
}
private void XAFDISolutionWindowsFormsApplication_CustomizeLanguagesList(object sender, CustomizeLanguagesListEventArgs e)
{
string userLanguageName = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
if (userLanguageName != "en-US" && e.Languages.IndexOf(userLanguageName) == -1)
{
e.Languages.Add(userLanguageName);
}
}
}
And Program.cs
static class Program
{
[STAThread]
static void Main()
{
var unityContainer = new UnityContainer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
EditModelPermission.AlwaysGranted = System.Diagnostics.Debugger.IsAttached;
string connectionString = null;
if (ConfigurationManager.ConnectionStrings["ConnectionString"] != null)
connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var winApplication = new XAFDISolutionWindowsFormsApplication(unityContainer);
winApplication.ConnectionString = connectionString;
try
{
winApplication.Setup();
winApplication.Start();
}
catch (Exception e)
{
winApplication.HandleException(e);
}
}
}
Lets rock!
In our platform agnostic module we create a simple BO:
[Persistent]
[DefaultClassOptions]
public class MyBo1 : XPObject
{
public MyBo1()
{
}
public MyBo1(Session session) : base(session)
{
}
public MyBo1(Session session, XPClassInfo classInfo) : base(session, classInfo)
{
}
[NonPersistent]
[MemberDesignTimeVisibility(false)]
public IUnityContainer UnityContainer
{
get { return (Session as IUnityContainerProvider).UnityContainer; }
}
private string _MyName;
[Size(SizeAttribute.Unlimited)]
[Persistent]
public string MyName
{
get { return _MyName; }
set { SetPropertyValue("MyName", ref _MyName, value); }
}
[DevExpress.Persistent.Base.Action(Caption = "Rename Me!!!")]
public void RenameMe()
{
UnityContainer.Resolve<IRenamer>().RenameMe(this);
}
}
Notice there is a MethodAction that pulls out the dependency of `IRenamer'
public interface IRenamer
{
void RenameMe(MyBo1 myBo1);
}
And a NullImplementation
public class NullRenamer : IRenamer
{
[Dependency]
public IObjectSpace OS { get; set; }
public void RenameMe(MyBo1 myBo1)
{
//I should never be called.
}
}
So we have a nice NullImplementation and we don't have to check allways if the dependency is already registered (performance).
In the Module we implement the interface IUnityModule
and register the type of the NullRenamer
public sealed partial class XAFDISolutionModule : ModuleBase, IUnityModule
{
public XAFDISolutionModule()
{
InitializeComponent();
}
public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB)
{
ModuleUpdater updater = new DatabaseUpdate.Updater(objectSpace, versionFromDB);
return new ModuleUpdater[] { updater };
}
public void InitUnityContainer(Microsoft.Practices.Unity.IUnityContainer unityContainer)
{
unityContainer.RegisterType<IRenamer, NullRenamer>();
}
public void UnityContainerInitialized(Microsoft.Practices.Unity.IUnityContainer unityContainer)
{
}
}
In the WinProject we create a new DomainLogic class called WinRenamer
public class WinRenamer : IRenamer
{
[Dependency]
public IObjectSpace OS { get; set; }
public void RenameMe(MyBo1 myBo1)
{
//I should be be called.
myBo1.MyName = "Hello from the Win Project: My Dependencies: " + GetType().FullName + " " + OS + "Session id:" + (OS as XPObjectSpace).Session;
}
}
And the WinModule need's to overwrite the IRenamer
registration
[ToolboxItemFilter("Xaf.Platform.Win")]
public sealed partial class XAFDISolutionWindowsFormsModule : ModuleBase, IUnityModule
{
public XAFDISolutionWindowsFormsModule()
{
InitializeComponent();
}
public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB)
{
return ModuleUpdater.EmptyModuleUpdaters;
}
public void InitUnityContainer(IUnityContainer unityContainer)
{
unityContainer.RegisterType<IRenamer, WinRenamer>();
}
public void UnityContainerInitialized(IUnityContainer unityContainer)
{
}
}
Thats it!
Check out the video on Screencast And the source-code on Bitbucket