Am Mittwoch hat die .NET User Group Hamburg das zweite .NET Coding Dojo in wunderbarer Lage im Neuen Wall durchgeführt. Der Dank gilt diesmal Computer Futures, die uns freundlicherweise Raum, Beamer und Getränke zur Verfügung gestellt haben.
Ablauf
Nach einer kurzen Reflektion des letzten Treffens und Klärung der aufgetretenen Syntaxprobleme hat sich das Team dafür entschieden, die Kata.RomanNumerals zu implementieren. Wie beim ersten Treffen haben wir diese relativ einfache Kata auch wieder im Randori-Stil durchgeführt. Da das Team relativ gleich geblieben ist, konnten wir den Ablauf jedoch leicht erweitern. Um etwas mehr Schwung in die Test-Phase zu bringen, habe ich vorgeschlagen immer in der Rot-Phase zu rotieren. Zusätzlich haben wir git als Versionskontrollsystem im Einsatz gehabt und bei jeder Grün-Phase sowie nach Refactorings commited. Das Ergebnis kann auf meinem Github-Account bewundert werden.
Kata
Aus meiner Sicht bildet die Kata.RomanNumerals eine ideale Fortsetzung zur Kata.StringReplacer, die wir beim ersten Treffen hatten und ist praktisch perfekt mit Test-Driven Development lösbar. Insbesondere das nachträgliche Implementieren der scheinbar komplizierten Subtraktionsregel ist wunderbar anhand von wenigen Sonderfällen (= UnitTests) lösbar.
Was lief gut?
- Die Rotation der Teilnehmer klappte wieder problemlos – Danke Jungs đ
- TDD wurde als sehr erfolgreich für die Kata angesehen
- Nahezu alle Teilnehmer waren überrascht wie einfach sich die Subtraktionsregel in TDD implementieren lässt
- Die Randori-Form mit Rotation bei Rot sorgt für Aktivität
- Es wurde immer versuchen die einfachste Lösung zu wählen
- Die Rollen Driver, Co-Pilot und Publikum wurden klarer getrennt
- Es wurde nach jedem implementierten Test in das git Repository commited
- Ganz wichtig: Es hat wieder viel Spaß gemacht!
Was wollen wir besser machen?
- Noch mehr Wert auf das Naming legen
- Überraschenderweise hat sich kein Team getraut die Namenskonvention des ersten Teams zu verbessern
- Insbesondere auch die Commit-Kommentare sind noch nicht sehr aussagekräftig
- In den Refactoring-Phasen sollten die Tools (Resharper) besser genutzt werden
- Die Refactorings könnten noch ausführlicher diskutiert werden
- Von Anfang an externe Tastatur und Maus für den Laptop bereit stellen
- Internetverbindung um Syntaxfrage schneller klären zu können
- Parametrisierte Tests benutzen um weniger Test-Codierungsaufwand zu haben
Vielen Dank an alle Teilnehmer â wir sehen uns dann beim nächsten Dojo.
Tags:
.NET User Group Hamburg,
CodingDojo
Gestern fand das erste .NET Coding Dojo in Hamburg statt und es war aus meiner Sicht ein voller Erfolg. Als erstes möchte ich mich an dieser Stelle bei der Firma masters of arts für die Räumlichkeit und natürlich bei allen Teilnehmer für die aktive Mitarbeit bedanken.
Durchgeführt haben wir das Dojo im Randori-Stil, d.h. es gab einen Entwickler, einen âCo-Pilotenâ und das Publikum. Nach jeweils 5 Minuten bzw. jedem neuen Testfall wurden die Positionen mit neuen Teilnehmer aus dem Publikum getauscht.
Nach einer kurzen allgemeinen Einführung in das Thema âCoding Dojoâ habe ich die Aufgabenstellungen der Kata.FizzBuzz und Kata.DictionaryReplacer vorgestellt. Durch kurze Abstimmung haben wir dann (vermutlich durch meine Wortwahl beeinflusst) die Kata.DictionaryReplacer gewählt.
Mit der Wahl dieser Kata war ich nicht ganz unglücklich, da wir DictionaryReplacer schon im Online Dojo durchgenommen haben und ich deshalb die Fallstricke schon etwas einschätzen konnte.
Kata.DictionaryReplacer
Create a method that takes a string and a dictionary,
and replaces every key in the dictionary pre and suffixed with a dollar sign,
with the corresponding value from the Dictionary.
Tests
input : "",
dict empty
output: ""
input : "$temp$",
dict ["temp", "temporary"]
output: "temporary"
input : "$temp$ here comes the name $name$",
dict ["temp", "temporary"] ["name", "John Doe"]
output : "temporary here comes the name John Doe"
[codingdojo.org]
Nach kurzer Einführung in die Vorgehensweise des Test-Driven Developments (TDD) und Diskussion über sinnvolles Naming ging es dann auch richtig in die Entwicklung. Die Kata eignet sich in dieser Form auch sehr schön um tatsächlich âTest firstâ zu arbeiten. Die Implementierung folgt quasi als logische Konsequenz.
Interessant und ungleich komplizierter wurde es dann als wir eine zusätzliche Anforderung eingebaut haben. Da es sich bei der Kata um eine Art Templatesystem handelt liegt es nahe auch in den Values des Wörterbuchs neue Keys zu erlauben und somit eine beliebige Ersetzungstiefe zu erreichen. Diese Anforderung ist ein wunderbares Beispiel dafür wie sehr man mit Aufwandsabschätzungen daneben liegen kann. đ
Die rekursive Ersetzung wurde noch sehr schnell gelöst, aber richtig schwer haben wir uns dann mit der Erkennung von zyklischen Ersetzungen und der daraus folgenden Endlosschleife getan. Nach und nach wurden immer kompliziertere Lösungsmöglichkeiten genannt, die aber in der gegebenen Zeit nicht fehlerfrei umgesetzt werden konnten. Als Folge haben wir immer noch einen Test ârotâ. Wer Lust hat kann sich also die Quellen vom github Repository ziehen und als Hausaufgabe den verbleibenden Test âgrünâ machen.
In der Nachbetrachtung haben wir noch einige Sachen festgestellt:
Was lief gut?
- Die Rotation der Teilnehmer klappte problemlos
- Lösungsideen sprudelten förmlich aus dem Publikum
- Interessante Edge-Cases wurden gefunden
- Viele kleine Tipps (z.B. Resharper Shortcuts) wurden ausgetauscht
- TDD wurde als erfolgreich für die Kata angesehen
- Die Randori-Form sorgt für Aktivität
- Ganz wichtig: Es hat Spaß gemacht!
Was wollen wir besser machen?
- Schneller den ersten Test schreiben
- Intensivere Diskussion des Interfaces
- Noch mehr Wert auf das Naming legen
- Versuchen die einfachste Lösung zu wählen
- Mehr Wert auf Refactoring legen
- wurde etwas vernachlässigt, da wir viel Zeit an der Endlosschleife verbraucht haben
- Die Rollen Driver, Co-Pilot und Publikum klarer trennen
- Nach jedem implementierten Testcase bzw. jeder Rotation der Teilnehmer in das git Repository einchecken
- Von Anfang an externe Tastatur und Maus für den Laptop bereit stellen
- Internetverbindung um Syntaxfrage schneller klären zu können
Als möglichen Termin für das Dojo haben wir jetzt grob jeweils den 3. Mittwoch im Monat vorgeschlagen. Über die Xing-Gruppe der .NET User Group Hamburg werden wir natürlich auch wieder entsprechende Einladungen verschicken.
Tags:
.NET User Group Hamburg,
CodingDojo,
TDD,
Test Driven Design
I recently had the problem to register a global hotkey, but my âoldâ Win32-API calls didnât work with WPF. I looked around the web and found the âManaged Windows APIâ, but I didnât want to add another external dependency to my project, so I extracted the core functions for registering hotkeys and condensed the code to a new version.
As the original âManaged Windows APIâ is licensed by the GNU Lesser General Public License (LGPL) I want to provide my modifications here.
First we need a Window which can dispatch the window messages to our event handlers:
/// <summary>
/// A Win32 native window that delegates window messages to
/// handlers. So several
/// components can use the same native window to save
/// "USER resources". This class
/// is useful when writing your own components.
/// </summary>
public sealed class EventDispatchingNativeWindow : NativeWindow
{
private static readonly Object MyLock = new Object();
private static EventDispatchingNativeWindow _instance;
/// <summary>
/// Create your own event dispatching window.
/// </summary>
public EventDispatchingNativeWindow()
{
CreateHandle(new CreateParams());
}
/// <summary>
/// A global instance which can be used by components
/// that do not need their own window.
/// </summary>
public static EventDispatchingNativeWindow Instance
{
get
{
lock (MyLock)
{
return
_instance ??
(_instance =
new EventDispatchingNativeWindow());
}
}
}
/// <summary>
/// Attach your event handlers here.
/// </summary>
public event WndProcEventHandler EventHandler;
/// <summary>
/// Parse messages passed to this window and send
/// them to the event handlers.
/// </summary>
/// <param name="m">A System.Windows.Forms.Message
/// that is associated with the
/// current Windows message.</param>
protected override void WndProc(ref Message m)
{
bool handled = false;
if (EventHandler != null)
EventHandler(ref m, ref handled);
if (!handled)
base.WndProc(ref m);
}
}
Now we can write our global hotkey handler class:
/// <summary>
/// Specifies a class that creates a global keyboard hotkey.
/// </summary>
public class GlobalHotkey
{
private static readonly Object MyStaticLock = new Object();
private static int _hotkeyCounter = 0xA000;
private readonly int _hotkeyIndex;
private readonly IntPtr _hWnd;
/// <summary>
/// Initializes a new instance of this class.
/// </summary>
/// <param name="keys">The keys.</param>
/// <param name="ctrl">if <c>true</c> [CTRL].</param>
/// <param name="alt">if <c>true</c> [alt].</param>
/// <param name="shift">if <c>true</c> [shift].</param>
/// <param name="winKey">if <c>true</c> [win key].</param>
public GlobalHotkey(Keys keys, bool ctrl,
bool alt, bool shift, bool winKey)
{
KeyCode = keys;
Ctrl = ctrl;
Alt = alt;
Shift = shift;
WindowsKey = winKey;
EventDispatchingNativeWindow.Instance.EventHandler
+= NwEventHandler;
lock (MyStaticLock)
{
_hotkeyIndex = ++_hotkeyCounter;
}
_hWnd = EventDispatchingNativeWindow.Instance.Handle;
Enable();
}
/// <summary>
/// The key code of the hotkey.
/// </summary>
public Keys KeyCode { get; private set; }
/// <summary>
/// Whether the shortcut includes the Control modifier.
/// </summary>
public bool Ctrl { get; private set; }
/// <summary>
/// Whether this shortcut includes the Alt modifier.
/// </summary>
public bool Alt { get; private set; }
/// <summary>
/// Whether this shortcut includes the shift modifier.
/// </summary>
public bool Shift { get; private set; }
/// <summary>
/// Whether this shortcut includes the Windows key modifier.
/// </summary>
public bool WindowsKey { get; private set; }
~GlobalHotkey()
{
Disable();
EventDispatchingNativeWindow.Instance.EventHandler
-= NwEventHandler;
}
/// <summary>
/// Enables the hotkey. When the hotkey is enabled,
/// pressing it causes a
/// <c>HotkeyPressed</c> event instead of being
/// handled by the active application.
/// </summary>
private void Enable()
{
// register hotkey
int fsModifiers = 0;
if (Shift) fsModifiers += ModShift;
if (Ctrl) fsModifiers += ModControl;
if (Alt) fsModifiers += ModAlt;
if (WindowsKey) fsModifiers += ModWin;
bool success =
RegisterHotKey(_hWnd, _hotkeyIndex,
fsModifiers, (int) KeyCode);
if (!success)
throw new
Exception(
"Could not register hotkey (already in use).");
}
/// <summary>
/// Disables this instance.
/// </summary>
private void Disable()
{
// unregister hotkey
UnregisterHotKey(_hWnd, _hotkeyIndex);
}
/// <summary>
/// Occurs when the hotkey is pressed.
/// </summary>
public event EventHandler HotkeyPressed;
private void NwEventHandler(ref Message m, ref bool handled)
{
if (handled) return;
if (m.Msg != WmHotkey ||
m.WParam.ToInt32() != _hotkeyIndex)
return;
if (HotkeyPressed != null)
HotkeyPressed(this, EventArgs.Empty);
handled = true;
}
#region PInvoke Declarations
private const int ModAlt = 0x0001;
private const int ModControl = 0x0002;
private const int ModShift = 0x0004;
private const int ModWin = 0x0008;
private const int WmHotkey = 0x0312;
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd,
int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd,
int id);
#endregion
}
Now we can easily register a global hotkey or use it as an observable in F#:
/// system-wide keyboard hook
let hotkey = new GlobalHotkey(Keys.Q,true,false,false,false)
let hookObserver =
hotkey.HotkeyPressed
|> Observable.subscribe (fun _ -> printfn "Hotkey pressed")
Tags:
C#,
F#,
Hotkey,
PInvoke,
win32
Friday I attended the .NET BootCamp âNHibernate vs. Entity Frameworkâ in Leipzig and as always it was a pleasure for me being there. Afterwards I had a nice talk with my friend Alexander Groß about the Open/Closed Principle. I didnât really care about this principle before, but now I think itâs really a nice idea:
âIn object-oriented programming, the open/closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"; that is, such an entity can allow its behaviour to be modified without altering its source code.â
[Wikipedia]
If we follow this principle we get lotâs of small and testable classes. I want to demonstrate this with a simple spam checker for mails.
Letâs say our mail class has only a sender, a recipient, a subject and the mail body:
public class EMail
{
public string Sender { get; set; }
public string Recipient { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public EMail(string sender, string recipient,
string subject, string body)
{
Sender = sender;
Recipient = recipient;
Subject = subject;
Body = body;
}
}
public enum SpamResult
{
Spam,
Ok,
Unknown
}
Now we want to know if a mail is spam or not. Of course we need some rules and some kind of ârule checkerâ to decide this. Here is a very naĂŻve implementation for this:
public class RuleChecker
{
public SpamResult CheckMail(EMail mail)
{
var result = TestRule1(mail);
if(result != SpamResult.Unknown)
return result;
result = TestRule2(mail);
if (result != SpamResult.Unknown)
return result;
// …
return SpamResult.Unknown;
}
private SpamResult TestRule1(EMail mail)
{
// I donât care about the concrete rules
}
private SpamResult TestRule2(EMail mail)
{
// I donât care about the concrete rules
}
}
It is obvious that this implementation breaks the Open/Closed Principle. Every time someone comes up with a new anti-spam rule or the rule priorities change I have to modify the code in CheckMail(). Another problem here is that I canât test CheckMail() isolated from the concrete rules.
With the help of the Open/Closed Principle our implementation could look like this:
public interface ISpamRule
{
SpamResult CheckMail(EMail mail);
}
public class RuleChecker
{
private readonly IEnumerable<ISpamRule> _rules;
public RuleChecker(IEnumerable<ISpamRule> rules)
{
_rules = rules;
}
public SpamResult CheckMail(EMail mail)
{
foreach (var rule in _rules)
{
var result = rule.CheckMail(mail);
if (result != SpamResult.Unknown)
return result;
}
return SpamResult.Unknown;
}
}
Now you could easily write isolated UnitTests for RuleChecker.CheckMail() and for every new rule.
You get the your concrete RuleChecker by calling the constructor with a list of rules:
class MyFirstRule : ISpamRule
{
public SpamResult CheckMail(EMail mail)
{
// I don’t care about this
}
}
class MySecondRule : ISpamRule
{
public SpamResult CheckMail(EMail mail)
{
// I don’t care about this
}
}
// …
var ruleChecker =
new RuleChecker(
new List<ISpamRule>
{
new MyFirstRule(),
new MySecondRule(),
// …
});
Alex please correct me if Iâm wrong, but I think this is what you had in mind Friday.
As stated before, we end up writing lotâs of very small classes – mostly with only one (public) method. I think this is some kind of functional approach, the only question is how we glue our code entities together. Letâs look at the corresponding F# implementation:
type EMail =
{ Sender: string;
Recipient: string;
Subject: string;
Body: string}
type SpamResult =
| Spam
| OK
| Unknown
let checkMail rules (mail:EMail) =
let rec checkRule rules =
match rules with
| rule::rest ->
match rule mail with
| Unknown -> checkRule rest
| _ as other -> other
| [] â> Unknown
checkRule rules
The signature of checkMail is (EMail -> SpamResult) list -> EMail â> SpamResult, which means it takes a list of rules (as above the order is important) and a EMail and returns the SpamResult. In addition I exchanged the explicit foreach loop with a tail recursion to make it look more functional.
If I want a concrete rule checker I could use partial application:
let myFirstRule mail =
// I don’t care about this
let mySecondRule mail =
// I don’t care about this
// val ruleChecker : (EMail â> SpamResult)
let ruleChecker =
checkMail
[ myFirstRule;
mySecondRule]
As you can see the F# implementation is nearly the same as the C# implementation, just without explicitly wrapping our public method in classes. If we would use Reflector we would see that the F# compiler is building the classes around our functions. One could say if we follow the Open/Closed Principle we come to functional code or the other way around if we write functional code we automatically apply the Open/Closed Principle. I think thatâs why I really didnât care about this before.
Appendix
After thinking about this implementation and the extra type hint (see mail:EMail) I came up with a slightly more generic implementation:
type SpamResult =
| Spam
| OK
let checkRules rules element =
let rec checkRule rules =
match rules with
| rule::rest ->
match rule element with
| None -> checkRule rest
| _ as other -> other
| [] â> None
checkRule rules
Here I deleted the enum value for SpamResult.Unknown and used the standard None option. As a consequence the signature changed to: val checkRules : (‘a -> ‘b option) list -> ‘a -> ‘b option. The function still takes a list of rules and a element and returns a option value. Now the checkRules function works with every kind of rule result and takes arbitrary elements.
Tags:
.NET Usergroup Leipzig,
C-Sharp,
F#,
Open/Closed Principle,
SOLID
Yesterday I was talking about F# at the .NET Developer Group Braunschweig. It was my first talk completely without PowerPoint (just Live-Coding and FlipChart) and I have to admit this is not that easy. But the event was really a big fun and we covered a lot of topics like FP fundamentals, concurrency and domain specific languages (of course I showed âFAKE â F# Makeâ).
Now I have a bit time before I go to the next BootCamp in Leipzig. Today Christian Weyer will show us exciting new stuff about WCF and Azure.
In the meanwhile I will write here about another important question (see first article) from the F# BootCamp in Leipzig:
Question 4 â Try to explain âCurryingâ and âPartial Applicationâ. Hint: Please show a sample and use the pipe operator |>.
Obviously this was a tricky question for FP beginners. There are a lot of websites, which give a formal mathematical definition but donât show the practical application.
âCurrying ⌠is the technique of transforming a function that takes multiple arguments (or more accurately an n-tuple as argument) in such a way that it can be called as a chain of functions each with a single argumentâ
[Wikipedia]
I want to show how my pragmatic view of the terms here, so letâs consider this small C# function:
public int Add(int x, int y)
{
return x + y;
}
Of course the corresponding F# version looks nearly the same:
let add(x,y) = x + y
But letâs look at the signature: val add : int * int â> int. The F# compiler is telling us add wants a tuple of ints and returns an int. We could rewrite the function with one blank to understand this better:
let add (x,y) = x + y
As you can see the add function actually needs only one argument â a tuple:
let t = (3,4) // val t : int * int
printfn "%d" (add t) // prints 7 â like add(3,4)
Now we want to curry this function. If youâd ask a mathematician this a complex operation, but from a pragmatic view it couldnât be easier. Just remove the brackets and the comma â thatâs all:
let add x y = x + y
Now the signature looks different: val add : int -> int â> int
But whatâs the meaning of this new arrow? Basically we can say if we give one int parameter to our add function we will get a function back that will take only one int parameter and returns an int.
let increment = add 1 // val increment : (int -> int)
printfn "%d" (increment 2) // prints 3
Here âincrementâ is a new function that uses partial application of the curryied add function. This means we are fixing one of the parameters of add to get a new function with one parameter less.
But why are doing something like this? Wouldnât it be enough to use the following increment function?
let add(x,y) = x + y // val add : int * int -> int
let increment x = add(x,1) // val increment : int -> int
printfn "%d" (increment 2) // prints 3
Of course we are getting (nearly) the same signature for increment. But the difference is that we can not use the forward pipe operator |> here. The pipe operator will help us to express things in the way we are thinking about it.
Letâs say we want to filter all even elements in a list, then calculate the sum and finally square this sum and print it to the console. The C# code would look like this:
var list = new List<int> {4,2,6,5,9,3,8,1,3,0};
Console.WriteLine(Square(CalculateSum(FilterEven(list))));
If we donât want to store intermediate results we have to write our algorithm in reverse order and with heavily use of brackets. The function we want to apply last has to be written first. This is not the way we think about it.
With the help of curried functions, partial application and the pipe operator we can write the same thing in F#:
let list = [4; 2; 6; 5; 9; 3; 8; 1; 3; 0]
let square x = x * x
list
|> List.filter (fun x -> x % 2 = 0) // partial application
|> List.sum
|> square
|> printfn "%A" // partial application
We describe the data flow in exactly the same order we talked about it. Basically the pipe operator take the result of a function and puts it as the last parameter into the next function.
What should we learn from this sample?
- Currying has nothing to do with spicy chicken.
- The |> operator makes life easier and code better to understand.
- If we want to use |> we need curryied functions.
- Defining curryied functions is easy â just remove brackets and comma.
- We donât need the complete mathematical theory to use currying.
- Be careful with the order of the parameter in a curryied function. Donât forget the pipe operator puts the parameter from the right hand side into your function â all other parameters have to be fixed with partial application.
Tags:
.NET Developer Group Braunschweig,
.NET User Group Leipzig,
Currying,
F#,
F-sharp Make,
Fake,
partial application,
pipe,
|>
"Implementing Microsoft Dynamics NAV 2009" is a new book by David Roys (MVP for Dynamics NAV) and Vjekoslav Babic (Dynamics NAV consultant). It shows the new features of Dynamics NAV 2009 in step-by-step explanations of real-world examples.
If you are interested in this book you can read the complete seventh chapter right here on navision-blog.de:
Chapter 6 (âModifying the Systemâ) is also available for download.
What the book covers
Chapter 1
The purpose of this chapter is a teaser introduction to get you excited about the product, what’s in it in general, and what’s in it as compared to previous versions, to give you a little taste of what’s coming up in the book, and explain what the fuss about this new release is all about.
Chapter 2
The RoleTailored client is the new user interface for users of Microsoft Dynamics NAV 2009, and it is completely different to the pervious versions. We’ll take you through the different components of the interface, introduce the terminology, explore the navigation components and page types, and teach you how to personalize the application to meet your own requirements using the extensive personalization features.
Chapter 3
Microsoft Dynamics NAV 2009 introduces a new paradigm to ERP. Instead of the system being focused on the forms that capture and present data and the functions the user can perform, the system is based around the individuals within an organization, their roles, and the tasks they perform. We cover how Microsoft researched the roles and explore the departments, roles, and tasks that have been identified in the Microsoft Dynamics Customer Model. We also show the reader how to assign the standard roles to users, how to create new roles, and how to allow departmental super users to configure the application for their role so that the change is applied to all users with the same profile.
Chapter 4
Microsoft Dynamics NAV is not a product with a Next-Next-Finish type of installation, and it takes a lengthy project to deploy it successfully. We focus on the six phases of the implementation process, and explain each phase with detailed dos and don’ts for a typical implementation. Based on the Dynamics Sure Step implementation methodology with advice liberally sprinkled throughout, special attention is given to new features of Microsoft Dynamics NAV 2009, and where the new capabilities must be taken into account to make most out of the implementation project.
Chapter 5
Every implementation of Microsoft Dynamics NAV 2009 will require the system to be configured to meet the needs of the business. This chapter tells the implementation consultant how to do this from a core financials perspective and provides valuable information that will allow developers to understand more about the application they are changing. We cover basic accounting for programmers, dimensions, and posting groups, and how to use the Rapid Implementation Methodology (RIM) Toolkit to speed things along.
Hardly any standard system can fit the needs of a business out of the box. Either the customer must shape their processes to match the system, or the consultant must shape the system to match the processes, and usually the latter prevails. This chapter explains the process of modifying the system, how to design a viable data model, and how to design and develop a functional user interface for both RoleTailored and Classic clients, without writing any code.
The three-tiered architecture of Microsoft Dynamics NAV 2009 and native Web Services Enablement open up a whole new world of possibilities for NAV implementations. We cover some of the many possibilities for extending the application, allowing the consultant and developer to understand the technologies that are available and their respective design considerations. Our practical examples introduce the NAV programmer to the world of .NET and show how you can use the information available on the internet to develop your own killer .NET add-ons.
Chapter 8
There’s much more to development than programming. It starts with understanding what customer really needs, and usually extends way beyond the system being deployed to a test environment. This chapter focuses on the development phase, and what it takes to get from a concept to a live and working solution.
Chapter 9
After the system goes live, or as it grows, there are periods when new problems may arise, and often their source is far from obvious. This chapter explores the tools and techniques available for detecting problems, pinpointing the source, and helping to remove them from the system quickly and painlessly. It explains how to debug the Service Tier, how to troubleshoot performance issues, what can be done to avoid problems, and how proper planning before design can help to get it right the first time.
Chapter 10
Our sample application focuses on requirements gathering, functional specification creation, solution design, and the eventual build of a prototype. We look at how a business problem can be explored using techniques such as interviewing, use-case modeling, and object-role modeling to create a solution design that can be molded into a working prototype.
If you want to get more information about the book visit: http://www.packtpub.com/implementing-microsoft-dynamics-nav-2009/book
Tags:
Dynamics NAV 2009,
Navision