Affichage des articles dont le libellé est c#. Afficher tous les articles
Affichage des articles dont le libellé est c#. Afficher tous les articles

mardi 8 mars 2016

Commands you must know for ACMESharp

To complete the QuickStart from https://github.com/ebekker/ACMESharp/wiki/Quick-Start you may encounter some troubles. Among them:

----- I -----
The given key was not present in the dictionary

The solution is given by one of the issue.

This his formally an extract/copy from https://github.com/ebekker/ACMESharp/issues/101. From this link one can read:

There are a few bookkeeping cmdlets you can run to make sure the parameters to the Complete-* cmdlet are valid:
  • Get-ACMEIdentifier -- with no arguments, this cmdlet will list all the current Identifiers you have in the Vault; if you give it an Identifier reference (e.g. sequence number, Alias or GUID) it will give you more details about that particular Identifier
  • Get-ACMEChallengeHandlerProfile -ListChallengeTypes -- this will return the list of all Challenge Types that are currently registered and available in the current PS session, e.g. dns-01, http-01
  • Get-ACMEChallengeHandlerProfile -ListChallengeHandlers -- this will return the list of all Challenge Handlers that are currently registered and available in the current PS session, e.g. manual, iis, awsRoute53, awsS3
  • Get-ACMEChallengeHandlerProfile -GetChallengeHandler iis -ParametersOnly -- this form of the cmdlet will list details about the parameters that must or may be specified for the named Challenge Handler type
With these cmdlets you can ensure that the parameters to your Complete-ACMEChallenge call are all correct, for example that the Identifier reference is valid. If you run these and everything seems to be specified correctly and you still get this error, then there might be a bug somewhere and we can trace through that.

----- II -----
Stuck in the pending status

The solution can be found here: http://stackoverflow.com/questions/35302044/letsencrypt-acmesharp-client-challenge-in-pending-state-for-over-an-hour

In short using the following command : (Update-ACMEIdentifier dns1 -ChallengeType http-01).Challenges should exhibit that one among three of the challenges is valid.

----- III -----
Cannot export PKCS12; Issuer certificate hasn't been resolved

The solution can be found here: https://github.com/ebekker/ACMESharp/issues/87.

In short an Update-ACMECertificate certAlias solves the problem.

lundi 14 septembre 2015

Nearest neighborS search by Binary Partition Tree

There are a lot of excellent references on Internet, one of them is:

The missing part: some code to play around. Let's try to make something generic. What to we need to make a class partitionable ?

    public interface IIsPartionableByBT {
        Int32 Id { get; }

        float BTX { get; }
        float BTY { get; }
        float BTZ { get; }

        float DistTo(IIsPartionableByBT c);
    }

    public delegate float BTCooProjector(IIsPartionableByBT coo);

We also need Cells/Nodes for the tree:

    public class MbtBSPNode<T> where T :  IIsPartionableByBT {
        public MbtBSPNode<T> Parent { get; set; }
        public T Cell { get; set; }
        public MbtBSPNode<T> Left { get; set; }
        public MbtBSPNode<T> Right { get; set; }

        public ICollection<MbtBSPNode<T>> Cells { get; set; }

        public UInt32 Depth { get { if (Parent == null) return 0; return Parent.Depth + 1; } }
    }

And the tree himself:

    public class MbtBSPTree<T> where T : class, IIsPartionableByBT {
        public MbtBSPNode<T> Root { get; private set; }

        public UInt16 SplitDepthBy { get; private set; }

        public UInt16 LimitDepthTo { get; private set; }

        public MbtBSPTree(IEnumerable<T> l, UInt16 splitDepthBy = 0, UInt16 limitDepthTo = 0) {
            if (SplitDepthBy == 0) {
                if (l.Min(x => x.BTZ) == l.Max(x => x.BTZ)) {
                    SplitDepthBy = 2;
                } else {
                    SplitDepthBy = 3;
                }
            } else {
                SplitDepthBy = splitDepthBy;
            }
            LimitDepthTo = limitDepthTo;

            Root = BuildBSPTree(l, 0);
        }

        public BTCooProjector DefProjector(UInt16 axis) {
            switch (axis) {
                case 0:
                    return (n) => n.BTX;
                case 1:
                    return (n) => n.BTY;
                case 2:
                    return (n) => n.BTZ;
                default:
                    throw new Exception("valeur de split de profondeur non gérée");
            }
        }

        public MbtBSPNode<T> BuildBSPTree(IEnumerable<T> l, UInt16 depth, MbtBSPNode<T> p = null) {
            if (l.Count() == 0)
                return null;
            UInt16 axis = (UInt16)(depth % SplitDepthBy);

            MbtBSPNode<T> n = new MbtBSPNode<T>();
            n.Parent = p;

            if (LimitDepthTo > 0 && depth == LimitDepthTo) {
                n.Cells = new List<MbtBSPNode<T>>();
                foreach (T cc in l) {
                    n.Cells.Add(new MbtBSPNode<T> { Cell = cc });
                }
            } else {
                Double med = Double.MinValue;
                List<T> ll = new List<T>();
                List<T> lr = new List<T>();

                BTCooProjector proj = DefProjector((UInt16)(depth % SplitDepthBy));
                med = l.Median(x => proj(x));
                foreach (T c in l) {
                    if (proj(c) < med) {
                        ll.Add(c);
                    } else {
                        if (proj(c) == med) {
                            if (n.Cell == null)
                                n.Cell = c;
                            else
                                ll.Add(c);
                        } else {
                            lr.Add(c);
                        }
                    }
                }

                if (depth <= 2) {
                    //this will lead to 8 threads
                    Task<MbtBSPNode<T>> tl = Task<MbtBSPNode<T>>.Factory.StartNew(() =>
                        BuildBSPTree(ll, (UInt16)(depth + 1), n));
                    n.Right = BuildBSPTree(lr, (UInt16)(depth + 1), n);
                    //synchro thread
                    n.Left = tl.Result;
                } else {
                    n.Left = BuildBSPTree(ll, (UInt16)(depth + 1), n);
                    n.Right = BuildBSPTree(lr, (UInt16)(depth + 1), n);
                }
            }

            return n;
        }

#if DEBUG
        public Int32 ScannedNodeNumber { get; set; }
#endif

        public IEnumerable<MbtBSPNode<T>> GetNearestNodes(T cel, float radius) {
#if DEBUG
            ScannedNodeNumber = 0;
#endif
            MbtBSPNode<T> current;
            Queue<MbtBSPNode<T>> q = new Queue<MbtBSPNode<T>>();
            q.Enqueue(Root);

            while (q.Count > 0) {
                current = q.Dequeue();
                BTCooProjector proj;

                if (current.Cell != null) {
#if DEBUG
                    ScannedNodeNumber++;
#endif
                    if (cel.DistTo(current.Cell) <= radius && cel.Id != current.Cell.Id) {
                        yield return current;
                    }

                    proj = DefProjector((UInt16)(current.Depth % SplitDepthBy));

                    if (proj(cel) - radius <= proj(current.Cell)) {
                        if (current.Left != null)
                            q.Enqueue(current.Left);
                    }
                    if (proj(cel) + radius > proj(current.Cell)) {
                        if (current.Right != null)
                            q.Enqueue(current.Right);
                    }
                }

                if (current.Cells != null) {
                    foreach (MbtBSPNode<T> cc in current.Cells) {
#if DEBUG
                        ScannedNodeNumber++;
#endif
                        if (cel.Id != cc.Cell.Id && cel.DistTo(cc.Cell) <= radius)
                            yield return cc;
                    }
                }

            }
        }

One can find a median algorithm in here:

jeudi 14 mai 2015

Coloring cells in an OpenXml SpreadsheetDocument

Tow days, and many readings. That's the time I needed to figure out how to set a cell color in an openXml spreadsheet.

It finally ends by the use of the OpenXml SDK Productivity Tool.

The main point seems to be that there must be a minimal stylesheet in the spreadsheet. Among other, this minimal stylesheet must comprise s two Fills. This styleshett may be generated by the following code:

private void GenerateWorkbookStylesPartContent(WorkbookPart workbookPart, String partId) {
    WorkbookStylesPart wsp = workbookPart.AddNewPart(partId);

    Stylesheet stylesheet = new Stylesheet() { 
        MCAttributes = new MarkupCompatibilityAttributes() { Ignorable = "x14ac" } };
    stylesheet.AddNamespaceDeclaration("mc", 
        "http://schemas.openxmlformats.org/markup-compatibility/2006");
    stylesheet.AddNamespaceDeclaration("x14ac", 
        "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");

    Fonts fonts = new Fonts() { Count = (UInt32Value)1U, KnownFonts = true };
    Font font = new Font();
    FontSize fontSize = new FontSize() { Val = 11D };
    Color color = new Color() { Theme = (UInt32Value)1U };
    FontName fontName = new FontName() { Val = "Calibri" };
    FontFamilyNumbering fontFamilyNumbering = new FontFamilyNumbering() { Val = 2 };
    FontScheme fontScheme = new FontScheme() { Val = FontSchemeValues.Minor };
    font.Append(fontSize);
    font.Append(color);
    font.Append(fontName);
    font.Append(fontFamilyNumbering);
    font.Append(fontScheme);
    fonts.Append(font);
    stylesheet.Fonts = fonts;

    Borders borders = new Borders() { Count = (UInt32Value)1U };
    Border border = new Border();
    LeftBorder leftBorder = new LeftBorder();
    RightBorder rightBorder = new RightBorder();
    TopBorder topBorder = new TopBorder();
    BottomBorder bottomBorder = new BottomBorder();
    DiagonalBorder diagonalBorder = new DiagonalBorder();
    border.Append(leftBorder);
    border.Append(rightBorder);
    border.Append(topBorder);
    border.Append(bottomBorder);
    border.Append(diagonalBorder);
    borders.Append(border);
    stylesheet.Borders = borders;

    stylesheet.Fills = new Fills();
    Fill f = new Fill { PatternFill = 
        new PatternFill { PatternType = PatternValues.None}};
    stylesheet.Fills.Append(f);
    stylesheet.Fills.Append(new Fill { PatternFill = 
        new PatternFill { PatternType = PatternValues.Gray125 } });

    CellFormats cellFormats = new CellFormats() { Count = (UInt32Value)1U };
    CellFormat cellFormat = new CellFormat() { 
        NumberFormatId = (UInt32Value)0U, 
        FontId = (UInt32Value)0U, 
        FillId = (UInt32Value)0U, 
        BorderId = (UInt32Value)0U, 
        FormatId = (UInt32Value)0U };
    cellFormats.Append(cellFormat);
    stylesheet.CellFormats = cellFormats;

    CellStyles cellStyles = new CellStyles() { Count = (UInt32Value)1U };
    CellStyle cellStyle = new CellStyle() { 
        Name = "Normal", FormatId = (UInt32Value)0U, BuiltinId = (UInt32Value)0U };
    stylesheet.CellStyles = cellStyles;

    CellStyleFormats cellStyleFormats = new CellStyleFormats() { Count = (UInt32Value)1U };
    CellFormat cellFormat2 = new CellFormat() { 
        NumberFormatId = (UInt32Value)0U, 
        FontId = (UInt32Value)0U, 
        FillId = (UInt32Value)0U, 
        BorderId = (UInt32Value)0U };
    cellStyleFormats.Append(cellFormat2);
    stylesheet.CellStyleFormats = cellStyleFormats;

    cellStyles.Append(cellStyle);

    wsp.Stylesheet = stylesheet;
}

From here, all what remain to do is handle the color in the cells, during the process I use a dictionary to avoid querying the stylesheet. In my case the key type is System.Drawing.Color because I'm exporting a DataGridView but, of course this type can be of any type you need.

    private Dictionary _colors = 
        new Dictionary();

Then, somewhere in the code:

UInt32 cellStyleUid = 0;
if ( col != System.Drawing.Color.Transparent) {
    if (!_colors.ContainsKey(col)) {
        //that is the style does not exists for this color
        if (_ssDoc.WorkbookPart.WorkbookStylesPart == null) {
            GenerateWorkbookStylesPartContent(_ssDoc.WorkbookPart, "rId5");
        }

        //Create the Fill
        Fill fill = new Fill();
        PatternFill pf = new PatternFill { PatternType = PatternValues.Solid };
        ForegroundColor fgc = new ForegroundColor { 
             Rgb = HexBinaryValue.FromString(Convert.ToString(col.ToArgb(), 16)) };
        BackgroundColor bgc = new BackgroundColor() { Indexed = (UInt32Value)64U };
        pf.Append(fgc);
        pf.Append(bgc);
        fill.Append(pf);
        //update the stylesheet
        _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.Fills.Append(fill);
        Int32 iFill = _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.Fills.Count() - 1;
        _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.Fills.Count = (UInt32)(iFill + 1);

        //Create the CellFormat to use the created Fill
        CellFormat lcf = 
            (CellFormat)_ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.CellFormats.LastChild;
        CellFormat cf = new CellFormat { 
            NumberFormatId = lcf.NumberFormatId,
            FontId = lcf.FontId,
            FillId = (UInt32Value)(UInt32)iFill,
            BorderId = lcf.BorderId,
            FormatId = lcf.FormatId,
            ApplyFill = true,
            ApplyFont = lcf.ApplyFont
        };                   

        //update the stylesheet 
        _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.CellFormats.Append(cf);
        Int32 iCellFormat = 
            _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.CellFormats.Count() - 1;
        _ssDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.CellFormats.Count = 
            (UInt32)(iCellFormat + 1);

        //put the index of the new CellFormat in the buffer
        _colors.Add(col, (UInt32)iCellFormat);

    }
    //retrieve the index of the cell format for the color
    cellStyleUid = _colors[col];
}

It remains to use the updated stylesheet at the cell level. With cell being of type DocumentFormat.OpenXml.Spreadsheet.Cell

if (cellStyleUid != 0)
    cell.StyleIndex = cellStyleUid;

Et voilà !

lundi 27 avril 2015

Secure connection strings in app.config for a desktop application

The goal is to get away as little as possible of the standard.

In this case the standard is : Encrypting Configuration Information Using Protected Configuration

In short: use a ProtectedConfigurationProvider. The .Net Framework provides two of them:

The main inconvenience of those providers comes from the fact that they have been designed for web applications: that are applications in which the securisable is the application server. That is one web.config in one machine

With a desktop application you have a plurality of app.config

Said providers are machine based, i.e. there is one configuration file per machine. In other words, the configuration file of one machine can't be used by another because of different encryption keys.

Fortunately the .Net Framework provides an abstract class that can be overridden to built a custom provider.

Here is a sample

The DLL:

using System;
using System.Configuration;
using System.Xml;

namespace MBT.ProtectedConfigurationProviders {

    public class WeakProtectedConfigurationProvider : ProtectedConfigurationProvider {        
        public override void Initialize(string name, 
            System.Collections.Specialized.NameValueCollection config) {
            
            base.Initialize(name, config);
        }

        public override System.Xml.XmlNode Decrypt(System.Xml.XmlNode encryptedNode) {
            string decryptedData =
                DecryptString(encryptedNode.InnerText);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.LoadXml(decryptedData);

            return xmlDoc.DocumentElement;
        }

        public override System.Xml.XmlNode Encrypt(System.Xml.XmlNode node) {
            string encryptedData = EncryptString(node.OuterXml);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.LoadXml("" +
                encryptedData + "");

            return xmlDoc.DocumentElement;
        }

        public static String EncryptString(String st) {
            return Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(st));
        }

        public static String DecryptString(String st) {
            return WeakProtectedConfigurationProvider.GetString(Convert.FromBase64String(st));
        }

        public static byte[] GetBytes(string str) {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        public static string GetString(byte[] bytes) {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }
    }

}

Here the WEAK word is deliberately chosen. Base 64 encoding is not at all a cypher algorithm.

Then the Program:

using System;
using System.Configuration;

namespace bas {
    class Program {
        static void Main(string[] args) {
            //String cs = @"";
            //Console.WriteLine(Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(cs)));
            Console.WriteLine(ConfigurationManager.ConnectionStrings["SomeName"].ConnectionString);
        }
    }
}

And finally the app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration >
  <!-- xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"-->
   
  <configProtectedData defaultProvider="WeakProtectedConfigurationProvider">
    <providers>
      <clear />
      <add name ="WeakProtectedConfigurationProvider" type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider, WeakProtectedConfigurationProvider"/>
    </providers>
  </configProtectedData>

  <connectionStrings configProtectionProvider="WeakProtectedConfigurationProvider">
    <EncryptedData>
      <CipherData>
        <CipherValue>PABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAHMAPgA8AGEAZABkACAAbgBhAG0AZQA9ACIAUwBvAG0AZQBOAGEAbQBlACIAIABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAD0AIgBEAGEAdABhACAAUwBvAHUAcgBjAGUAPQBTAG8AbQBlAFMAZQByAHYAZQByADsASQBuAGkAdABpAGEAbAAgAEMAYQB0AGEAbABvAGcAPQBTAG8AbQBlAEQAYQB0AGEAQgBhAHMAZQA7AEkAbgB0AGUAZwByAGEAdABlAGQAIABTAGUAYwB1AHIAaQB0AHkAPQBUAHIAdQBlADsAQwBvAG4AbgBlAGMAdAAgAFQAaQBtAGUAbwB1AHQAPQAxADgAMAA7AE0AdQBsAHQAaQBwAGwAZQBBAGMAdABpAHYAZQBSAGUAcwB1AGwAdABTAGUAdABzAD0AVAByAHUAZQAiACAAcAByAG8AdgBpAGQAZQByAE4AYQBtAGUAPQAiAFMAeQBzAHQAZQBtAC4ARABhAHQAYQAuAFMAcQBsAEMAbABpAGUAbgB0ACIAIAAvAD4APAAvAGMAbwBuAG4AZQBjAHQAaQBvAG4AUwB0AHIAaQBuAGcAcwA+AA==</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>
  
</configuration>

At least two points are significant here. First:

<add name ="WeakProtectedConfigurationProvider"
    type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider,  
          WeakProtectedConfigurationProvider"/>

Here to avoid a GAC registration, on use the syntax "type, assembly"

Second:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

One may want to use a namespace in configuration tag of app.config to avoid Visual Studio complaint. But, in my case this also leads to side effect: "side by side configuration error" which prevents application from starting. So, for now, I do without, and it runs well.

Thank you to StackOverflow for the StringToBytesToString.

jeudi 1 janvier 2015

Merging runs in an openxml file/document

The goal is to reduce the number of runs in the paragraphs of a document. I made the choice not to use the openxml SDK. Indeed my goal is a templating engine. In my process the runs may contain xsl, that is xml. So I made the choice to stay on raw XML tools.

The operational (without the usings, except those revealing a dependency) code looks like:

namespace SandBox {
    class Program {
        static void Main(string[] args) {
            try {

                String fileName = @"somepath\somefile.docx";
                String destFile = "res.docx";
                
                Tuple<XPathNavigator, XmlNamespaceManager> xp = 
                    ZDocx.GetNavigatorAndManagerFromString(
                        ZDocx.GetDocxDocumentStringFromDocxFile(fileName));
                XPathNavigator xpn = xp.Item1;
                XmlNamespaceManager xnm = xp.Item2;

                XPathNodeIterator xni = xpn.Select("//w:p", xnm);
                while (xni.MoveNext()) {
                    //Merge all runs ignoring styles
                    //ZDocx.MergeRuns(xni.Current);
                    //Merge considering only Bold as a grouping condition
                    ZDocx.MergeRuns(xni.Current, new ByStylesNodesComparator  {
                        Settings = new ByStylesNodesComparatorSettings {
                            CheckBold = true
                    }});                    
                }

                System.IO.File.Copy(fileName, destFile, true);
                ZDocx.SetDocxDocumentStringToDocxFile(xpn, destFile);

            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

There are 3 main steps:

  • I fristly load the document.xml part the openxml file in an XPathNavigator
  • Then I process each paragraph.
  • Finally I inject the modified document.xml in a new openxml file.

First the cooking code:

using Ionic.Zip;

namespace SandBox {
    public class ZDocx {
        public static String nsW = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

        public static String GetDocxDocumentStringFromDocxFile(String fileName) {
            String tf = System.IO.Path.GetTempFileName();
            StreamWriter sw = new StreamWriter(tf);
            using (ZipFile zip = ZipFile.Read(fileName)) {
                ZipEntry e = zip["word\\document.xml"];
                e.Extract(sw.BaseStream);                
            }
            try {
                sw.Close();
            } catch { }
            
            StreamReader sr = new StreamReader(tf);
            String st = sr.ReadToEnd();
            sr.Close();
            System.IO.File.Delete(tf);
            
            return st;
        }

        public static void SetDocxDocumentStringToDocxFile(XPathNavigator xdoc, String fileName) {            
            using (ZipFile zip = ZipFile.Read(fileName)) {
                using (MemoryStream ms = new MemoryStream()) {
                    XmlWriterSettings xws = new XmlWriterSettings {
                        Encoding = Encoding.UTF8
                    };
                    using (XmlWriter xw = XmlWriter.Create(ms, xws)) {
                        xw.WriteNode(xdoc, false);
                        xw.Flush();
                        ms.Position = 0;
                        zip.UpdateEntry("word\\document.xml", ms);
                        zip.Save();
                    }
                }
            }            
        }

        public static Tuple<XPathNavigator, XmlNamespaceManager> GetNavigatorAndManagerFromString(String st) {
            XmlDocument xml = new XmlDocument();
            StringReader stReader = new StringReader(st);
            XmlReader xReader = XmlReader.Create(stReader, new XmlReaderSettings() { 
                IgnoreWhitespace = false, 
                CloseInput = true });
            xml.Load(xReader);
            xReader.Close();
            XPathNavigator navXml = xml.CreateNavigator();
            XmlNamespaceManager manager = new XmlNamespaceManager(navXml.NameTable);
            manager.AddNamespace("w", nsW);
            manager.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");

            return new Tuple<XPathNavigator, XmlNamespaceManager>(navXml, manager);
        }

    }
}

Then the merging code:

namespace SandBox {
    public class ZDocx {
        public static void MergeRuns(XPathNavigator paragraph, INodeComparator areMergeable = null) {
            if (paragraph.LocalName != "p")
                throw new Exception("MergeRuns: paragraph is not a 'w:p'.");
            if (areMergeable == null)
                areMergeable = AlwaysTrueNodesComparator.GetInstance();

            XPathNavigator destRun = null;
            XPathNavigator lPara = paragraph.Clone();
            lPara.MoveToFirstChild();
            do {
                if (lPara.LocalName != "r")
                    continue;

                if (destRun == null) {
                    destRun = lPara.Clone();
                    continue;
                }

                if ( areMergeable.AreMergeable(destRun, lPara) ) {
                    XPathNavigator nL = lPara.Clone();
                    XPathNavigator nK = destRun.Clone();
                    nL.MoveToChild("t", nsW);
                    nK.MoveToChild("t", nsW);
                    nK.InnerXml += nL.InnerXml;                    
                    nL.MoveToParent();
                    nL.MoveToPrevious();
                    lPara.DeleteSelf();
                    lPara = nL;
                } else {
                    destRun = lPara.Clone();
                }

            } while (lPara.MoveToNext());
        }
    }
}
In the previous code, one key is the areMergeable parameter: this parameter allows to decide how the merging occurs. This parameter implements the following interface.
    public interface INodeComparator {
        Boolean AreMergeable(XPathNavigator xpn1, XPathNavigator xpn2);
    }
This interface may be implemented as in the following samples provided as an inspiration root:
namespace SandBox {
    public class AlwaysTrueNodesComparator : INodeComparator {
        private AlwaysTrueNodesComparator() {}

        private static AlwaysTrueNodesComparator _inst = new AlwaysTrueNodesComparator();
        public static AlwaysTrueNodesComparator GetInstance() {
            return _inst;
        }

        public Boolean AreMergeable(XPathNavigator xpn1, XPathNavigator xpn2) {
            return true;
        }
    }

    public class ByStylesNodesComparatorSettings {
        public Boolean CheckBold { get; set; }
        public Boolean CheckUnderlined { get; set; }
        public Boolean CheckItalic { get; set; }
        public Boolean CheckStriked { get; set; }
    }

    public class ByStylesNodesComparator : INodeComparator {
        public ByStylesNodesComparatorSettings Settings { get; set; }

        public Boolean AreMergeable(XPathNavigator xpn1, XPathNavigator xpn2) {
            XPathNavigator nav1 = xpn1.Clone();
            XPathNavigator nav2 = xpn2.Clone();

            Boolean nav1HasRPr = nav1.MoveToChild("rPr", ZDocx.nsW);
            Boolean nav2HasRPr = nav2.MoveToChild("rPr", ZDocx.nsW);

            Boolean b1, b2;

            if (Settings == null || Settings.CheckBold) {
                b1 = nav1HasRPr && nav1.SelectChildren("b", ZDocx.nsW).Count == 1;
                b2 = nav2HasRPr && nav2.SelectChildren("b", ZDocx.nsW).Count == 1;
                if (b1 != b2)
                    return false;
            }

            if (Settings == null || Settings.CheckUnderlined) {
                b1 = nav1HasRPr && nav1.SelectChildren("u", ZDocx.nsW).Count == 1;
                b2 = nav2HasRPr && nav2.SelectChildren("u", ZDocx.nsW).Count == 1;
                if (b1 != b2)
                    return false;
            }

            if (Settings == null || Settings.CheckItalic) {
                b1 = nav1HasRPr && nav1.SelectChildren("i", ZDocx.nsW).Count == 1;
                b2 = nav2HasRPr && nav2.SelectChildren("i", ZDocx.nsW).Count == 1;
                if (b1 != b2)
                    return false;
            }

            if (Settings == null || Settings.CheckStriked) {
                b1 = nav1HasRPr && nav1.SelectChildren("strike", ZDocx.nsW).Count == 1;
                b2 = nav2HasRPr && nav2.SelectChildren("strike", ZDocx.nsW).Count == 1;
                if (b1 != b2)
                    return false;
            }

            return true;
        }
    }
}

Take care to clone the navigators in the AreMergeable method to not surprise the caller.

Using AlwaysTrueNodesComparator reduces all paragraphs to one single run with the styles (or not) of the first run of the reduced paragraph.

Using ByStylesNodesComparator allows to merge runs according to some part of their styles. In the implemented class the handled style are Bold, Underline, Strike and Italic. Be careful that only basis underlining is handled. The underline style is not handled.

samedi 8 novembre 2014

Templating from Object

The idea here is to provide a really lightweight template engine.
The point here is to demonstrate how light such a code can be. In his current state the code suffers some defaults discussed at the end.
A template is a String comprising tokens of the form [ProperyName] or [ProperyName:format]. PropertyName can be any name of any property of an object or navigable property of the object. However IEnumerable are not handled.
public static String MergeObjectFromTemplate(String st, Object o) {
    if (String.IsNullOrEmpty(st)) throw new ArgumentNullException("st");
    if (o == null) return st;

    Int32 i = st.IndexOf("[");
    Int32 j = 0;
    String pName;
    Object lo;
    String[] a;
    while (i >= 0) {
        j = st.IndexOf("]");
        if (j == -1)
            return st;

        pName = st.Substring(i + 1, j - i - 1);
        a = pName.Split(':');
        lo = GetObjectByReflection(o, a[0]);
        if (lo == null) { 
            st = st.Replace("[" + pName + "]", "_" + pName + "_");
        } else {
            if (a.Count() == 1) {
                st = st.Replace("[" + pName + "]", lo.ToString().Trim());
            } else {
                st = st.Replace("[" + pName + "]", String.Format("{0:" + a[1] + "}", lo));
            }
        }

        i = st.IndexOf("[");
    }

    return st;
}
The key function here is: GetObjectByReflection.
public static Object GetObjectByReflection(Object src, String propertyName) {
    Type type;
    Object o = src;
    foreach (String fragment in propertyName.Split('.')) {
        type = o.GetType();
        PropertyInfo pi = type.GetProperty(fragment);
        if (pi == null) {
            o = null;
            break;
        }
        o = pi.GetValue(o, null);
        if (o == null) return String.Empty;
    }

    return o;
}
That is it!
Well, not totally. It remains some defaults, but for 50 lines, that's may be not so bad. Known defaults are:

  • Bad format string are not handled.
  • This is blocking, if throwing an exception is blocking. Correcting it is easy once expected behavior is known. At the time the code was updated I was just needing to format DateTime in a relatively safe environment: templates are edited by me. It is another story if you want to allow user to produce templates.
  • Excepted for a bad format String, the code always returns a String, even if a bad object is provided, or bad property name is used.

lundi 22 septembre 2014

Managing default values for entities in EF II: the just on time way

You probably still know how to create an entity with default values. This is the 'from birth' way.
Well, there is another way to set default values: a 'just on time way'.
The principle is to override the SaveChanges method to explore entities in the tracker to find the one having unset values, just like in the 'from birth way'. Here it is how it can be done.
        //the default value for datetime properties
        public DateTime DefaultDate { get; set; }

        public override int SaveChanges() {
            Boolean defaultSet = false;

            //seeking only pertinent entities
            foreach (DbEntityEntry dbee in ChangeTracker.Entries().
                     Where(x => x.State != EntityState.Deleted && x.State != EntityState.Detached ) ) {
                defaultSet = false;
                Object o = dbee.Entity;
                Type t = o.GetType();
                foreach ( MemberInfo m in t.GetProperties() ) {
                    PropertyInfo p = t.GetProperty(m.Name);
                    switch (Type.GetTypeCode(p.PropertyType)) {
                        case TypeCode.String:
                            if (p.GetValue(o, null) == null) {
                                p.SetValue(o, String.Empty, null);
                                defaultSet = true;
                            }
                            break;
                        case TypeCode.DateTime:
                            if ((DateTime)p.GetValue(o, null) == DateTime.MinValue) {
                                p.SetValue(o, DefaultDate, null);
                                defaultSet = true;
                            }
                            break;
                    }
                    
                    //update the state if needed
                    if ( defaultSet ) {
                        switch ( dbee.State ) {
                            case EntityState.Unchanged :
                                dbee.State = EntityState.Modified;
                                break;
                        }
                    }
                }
            }

            return base.SaveChanges();
        }
This could be used to implement business rules, such as LastModificationDate.
Before EF6, you may have to look for the tracker in the underlying ObjectContext
A full piece of code:
using System;
using System.Linq;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.Reflection;
using System.Data.Entity.Infrastructure;

namespace testef {
    public class Order {
        public Int32 Id { get; set; }
        public String Title { get; set; }

        public virtual ICollection Details { get; set; }

        public DateTime OrderDate { get; set; }

        public virtual OrderType Type { get; set; }
    }

    public class OrderType {
        public String Code { get; set; }
        public String Description { get; set; }
    }

    public class OrderDetail {
        public virtual Order Order { get; set; }
        public Int32 Id { get; set; }
        public String D { get; set; }
        public Boolean IsActive { get; set; }
    }

    public class OrderDetailConfiguration : EntityTypeConfiguration {
        public OrderDetailConfiguration()
            : base() {
            HasRequired(d => d.Order).WithMany(o => o.Details);
        }
    }

    public class OrderConfiguration : EntityTypeConfiguration {
        public OrderConfiguration()
            : base() {
            HasRequired(d => d.Type).WithMany();
        }
    }

    public class OrderTypeConfiguration : EntityTypeConfiguration {
        public OrderTypeConfiguration()
            : base() {
            Property(x => x.Code).HasColumnType("varchar").HasMaxLength(10);
            HasKey(x => x.Code);
        }
    }

    public class TestEFContext : DbContext {
        public DbSet Orders { get; set; }
        public DbSet Details { get; set; }

        public TestEFContext(String cs)
            : base(cs) {
            Database.SetInitializer(new DropCreateDatabaseAlways());
            //Database.SetInitializer(null);
            //Database.SetInitializer(new CreateDatabaseIfNotExists());
            //Database.SetInitializer(new CustomDataBaseInitializer());

        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        }

        public DateTime DefaultDate { get; set; }

        public override int SaveChanges() {

            Boolean defaultSet = false;
            foreach (DbEntityEntry dbee in ChangeTracker.Entries().
                     Where(x => x.State != EntityState.Deleted && x.State != EntityState.Detached ) ) {
                defaultSet = false;
                Object o = dbee.Entity;
                Type t = o.GetType();
                foreach ( MemberInfo m in t.GetProperties() ) {
                    PropertyInfo p = t.GetProperty(m.Name);
                    switch (Type.GetTypeCode(p.PropertyType)) {
                        case TypeCode.String:
                            if (p.GetValue(o, null) == null) {
                                p.SetValue(o, String.Empty, null);
                                defaultSet = true;
                            }
                            break;
                        case TypeCode.DateTime:
                            if ((DateTime)p.GetValue(o, null) == DateTime.MinValue) {
                                p.SetValue(o, DefaultDate, null);
                                defaultSet = true;
                            }
                            break;
                    }
                    if ( defaultSet ) {
                        switch ( dbee.State ) {
                            case EntityState.Unchanged :
                                dbee.State = EntityState.Modified;
                                break;
                        }
                    }
                }
            }

            return base.SaveChanges();
        }
    }

    public class CustomDataBaseInitializer : CreateDatabaseIfNotExists {
        public CustomDataBaseInitializer()
            : base() {
        }
    }
        
    class Program {
        static void Main(string[] args) {
            String cs = @"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
            using (TestEFContext ctx = new TestEFContext(cs)) {
                ctx.DefaultDate = new DateTime(2000, 1, 1);

                OrderType t = new OrderType { 
                    Code = "T1",
                    Description = "type One"
                };
                ctx.Orders.Add(new Order { 
                    Title = "first order",
                    Type = t
                });

                ctx.SaveChanges();
            }

            using (TestEFContext ctx = new TestEFContext(cs)) {
                Order o = ctx.Orders.First();
                Console.WriteLine(o.OrderDate);
            }
        }
    }
}

mardi 9 septembre 2014

Bulleting in MsWord openXml document, using OpenXml SDK: the basics.

As often the most important is the understanding of the model, here of the XML model.
This article is a simplifcation of Working with Numbered Lists in Open XML WordprocessingML.
Bulleting is a part of Numbering.
Numbering is a NumberingProperties (numPr) property in a ParagraphProperties property of a Paragraph from a Document object.
That is a hierarchy as follow:
<w:document>
    <w:body>
        <w:p>
            <w:pPr>
                <w:numPr>
                    <w:ilvl>X</w:ilvl>
                    <w:numId val="Y" />
numId leads to an NumberingInstance (num) in NumberingDefinitionsPart of MainDocumentPart of WordProcessingDocument.
//word/numbering.xml
    <w:numbering>
        <w:num w:numId="Y">
            <w:abstractNumId w:val="Z" />
abstractNumId leads to a AbstractNum (abstractNum)
//word/numbering.xml
    <w:numbering>
        <w:abstractNum w:abstractNumId="Z" >
            <w:lvl w:ilvl="X">
                ...
            </w:lvl>
        <w:num w:numId="Y">
            <w:abstractNumId w:val="Z" />

It is now clear that to get the bullet properties we have to traverse from the NumberingProperties of the Paragraph comprising the text of the bullet. Then we go to the NumberingDefinitionsPart searching the corresponding numId, That give us an abstractNumId which, with the ilvl of the ParagraphProperties, give us the Level we seek.
Remark
If you have a ParagraphProperties at the Paragraph level, the Paragraph stays a bullet (because of Paragraph.NumberingProperties), but the values from Paragraph.ParagraphProperties prevail over those of NumberingProperties.

mardi 1 juillet 2014

Filter DataGridView out of the DbContext

Imagine you load data into a DataGridView.DataSource from a database.
Then you want to allow end user to filter the results, but without hitting database again.
So you use a TextBox to prompt a filter an, OnTextChanged
private void tbFilter_TextChanged(object sender, EventArgs e) {
    CurrencyManager cm = (CurrencyManager)BindingContext[dgvList.DataSource];
    cm.SuspendBinding();
    foreach (DataGridViewRow r in dgvList.Rows) {
        r.Visible = String.IsNullOrEmpty(tbFilter.Text) || r.Cells[1].Value.ToString().Contains(tbFilter.Text);
    }
    cm.ResumeBinding();
}
Here the difficulty is the use of a CurrencyManager.

samedi 10 mai 2014

using UserManager/IdentityFramework with an Int32 as TKey for IUser

This article is about how to use Int32 as a key for users in asp.net identity.

First the model. As I seed it, you can also see how to instanciate the UserManage.

For this thank you to SymbolSource and to Microsoft of course!

namespace cclw4c.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser <Int32, CustomUserLogin, CustomUserRole, CustomUserClaim&gt: {
    }

    public class CustomRole : IdentityRole<Int32, CustomUserRole> {
        public CustomRole() { }
        public CustomRole(String name) { Name = name; }
    }

    public class CustomUserRole : IdentityUserRole<Int32> { }
    public class CustomUserClaim : IdentityUserClaim<Int32> { }
    public class CustomUserLogin : IdentityUserLogin<Int32> { }

    public class ApplicationUserDbContext : IdentityDbContext<ApplicationUser, CustomRole, Int32, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        public ApplicationUserDbContext()
            : base("ApplicationUser")
        {
            Database.SetInitializer<ApplicationUserDbContext>(new CreateDatabaseIfNotExistsWithSeedData());
        }

        
    }

    public class CreateDatabaseIfNotExistsWithSeedData : CreateDatabaseIfNotExists<ApplicationUserDbContext> {
        protected override void Seed(ApplicationUserDbContext context) {
            base.Seed(context);

            var user = new ApplicationUser() { UserName = "root" };
            UserManager<ApplicationUser, Int32> am = 
                new UserManager<ApplicationUser, Int32>(new UserStore<ApplicationUser, CustomRole, Int32, CustomUserLogin, CustomUserRole, CustomUserClaim>(context));
            IdentityResult ir = am.Create(user, "******");
            if (ir.Succeeded) {
                RoleManager<CustomRole, Int32> rm = new RoleManager<CustomRole, Int32>(new RoleStore<CustomRole, Int32, CustomUserRole>(context));
                rm.Create(new CustomRole("Root"));
                am.AddToRole(user.Id, "Root");
            }
        }
    }
}   
Second use
Int32.Parse(User.Identity.GetUserId()
every where it is needed.
Hightlight on UserManager instanciation
new UserManager<ApplicationUser, Int32>(new UserStore<
    ApplicationUser, 
    CustomRole, 
    Int32, 
    CustomUserLogin, 
    CustomUserRole, 
    CustomUserClaim
>(context));

lundi 25 mars 2013

Excecute SQL batch from C#

Two ways, extending two objects. For a SSMS like use, batchSeparator should be equal to go.

    public static class SomeExtensionsClass {
        public static void ExecuteBatchFromFile(this DataContext dc, String fileName, String batchSeparator) {
            StringBuilder sb = new StringBuilder();
            foreach (String sqlLine in File.ReadAllLines(fileName)) {
                if (sqlLine == batchSeparator) {
                    if (sb.Length != 0) {
                        dc.ExecuteCommand(sb.ToString());
                        sb.Remove(0, sb.Length); //On attend de passer en .Net 4 pour le .Clear
                    }
                } else {
                    sb.AppendLine(sqlLine);
                }
            }
            if (sb.Length != 0)
                dc.ExecuteCommand(sb.ToString());
        }

        public static void ExecuteBatchFromFile(this ObjectContext oc, String fileName, String batchSeparator) {
            StringBuilder sb = new StringBuilder();
            foreach (String sqlLine in File.ReadAllLines(fileName)) {
                if (sqlLine == batchSeparator) {
                    if (sb.Length != 0) {
                        oc.ExecuteStoreCommand(sb.ToString());
                        sb.Remove(0, sb.Length); //On attend de passer en .Net 4 pour le .Clear
                    }
                } else {
                    sb.AppendLine(sqlLine);
                }
            }
            if (sb.Length != 0)
                oc.ExecuteStoreCommand(sb.ToString());
        }
    }

lundi 21 novembre 2011

C# WPF, Dynamic Loading / Late Binding / Reflection with AvalonEdit

The point here is to optionnaly allow to use AvalonEdit (formerly known as Avalon) in an (wpf) application.


The basics

Specific using:
using System.IO;
using System.Reflection;
using System.Xml; //only for "the not so basic"
As you can see, none of them concerns AvanlonEdit...

When to Load :
String AvalonEditDllName = "ICSharpCode.AvalonEdit.dll";
if ( File.Exists(AvalonEditDllName) ) {/*...*/}

How to load:
Assembly u = Assembly.LoadFile(Path.GetFullPath(AvalonEditDllName));

What to instanciate:
Type tAvalonEditTextEditor = u.GetType("ICSharpCode.AvalonEdit.TextEditor");
if ( tAvalonEditTextEditor != null ) {/*...*/}

How to instanciate:
System.Windows.UIElement aeui = 
    (System.Windows.UIElement)Activator.CreateInstance(tAvalonEditTextEditor);

From now, you can use your System.Windows.UIElement in your WPF form just like this:
MainGrid.Children.Add(aeui);
where MainGrid is, for example, a System.Windows.Controls.Grid.

The basics of properties

Do you want line numbers ?
First let's look for the property of the type :
PropertyInfo propShowLineNumbers = 
    tAvalonEditTextEditor.GetProperty("ShowLineNumbers");
Then let's make the property true :
propShowLineNumbers.SetValue(aeui, true, null);

The not so basic of properties

And what about syntax hihgligting ?? Good question indeed !
Some more types and properties:
Type tAvalonEditHightingLoader = 
    u.GetType("ICSharpCode.AvalonEdit.Highlighting.Xshd.HighlightingLoader");
Type tAvalonEditHightingManager = 
    u.GetType("ICSharpCode.AvalonEdit.Highlighting.HighlightingManager");

PropertyInfo propSyntaxHighlighting = tAvalonEditTextEditor.GetProperty("SyntaxHighlighting");
PropertyInfo spropAEHMInstance = tAvalonEditHightingManager.GetProperty("Instance");

And the loading himself (I try to keep the variables consistant with the previous pieces of code):
String XshdFileName = "t-sql.xshd";
using ( XmlTextReader reader = new XmlTextReader(XshdFileName) ) {
    propSyntaxHighlighting.SetValue( //let's set the property of the text editor
        aeui,
        tAvalonEditHightingLoader.InvokeMember(
            "Load", //by loading the xshd file with HightingLoader.Load
            BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | 
                BindingFlags.InvokeMethod,
            null, null,
                   //with the following parameters
            new Object[] { reader, spropAEHMInstance.GetValue(null, null) }
                   //one of them being the static property
                   //HighlightingManager.Instance
        ),
        null
    );
 }

mercredi 2 novembre 2011

Sales Force and WCF

Hello,
The following is a 2 hours testing session resulting of the following sentence : "Our client wants to interface our Production tool with Sales Force...". Of course I heard of Sales Force before, but nothing more.
I assume that you have downloaded the wsdl file from your Sales Force Account.
Then you use the svcutil tool to generate your proxy class. Something like :
"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\svcutil" *.wsdl /language:C#

From this point you should realise that svcutil gives you a cs file but also an app.config part to use in your project to configure the proxy class.
Then the following code should list, on the console, the Accounts of your Sales Force Application. In this code "Soap" is a "false friend" coming from my minimalistic svcutil command line.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace sfBySoap {
  class Program {
    static void Main(string[] args) {
      try {
        SoapClient sfClient = new SoapClient();
        String sfLogin = "***@***.***";
        String sfPassword = "***";
        String sfSecurityToken = "***";
        LoginResult lr = sfClient.login(
          null, 
          sfLogin, 
          sfPassword + sfSecurityToken);
        Console.WriteLine("{0}\r\n{1}",
            lr.sessionId, 
            lr.serverUrl);
        SoapClient sc = new SoapClient(
            "Soap", 
            new EndpointAddress(new Uri(lr.serverUrl)));

        SessionHeader sh = new SessionHeader();
        sh.sessionId = lr.sessionId;

        QueryResult qr = sc.query(
              sh, //SessionHeader
              null, //QueryOptions
              null, //MruHeader
              null, //PackageVersion
              "select NAME, DESCRIPTION, TYPE, " +
                  "CREATEDBYID from Account");

        while (true) {
          foreach (Account a in qr.records) {
              Console.WriteLine("{0} - {1}\r\n    :{2}",
                  a.Name,
                  a.Type,
                  a.Description);
          }

          if (qr.done) {
              break;
          } else {
              qr = sc.queryMore(sh, null, qr.queryLocator);
          }
        }

        //sfClient.logout();
        sc.Close();
        sfClient.Close();
      } catch (Exception ex) {
          ExToConsole(ex);
      }
    }

    static void ExToConsole(Exception ex) {
        if (ex != null) {
            Console.WriteLine(ex.Message);
            ExToConsole(ex.InnerException);
        }
    }
  }
}

Here it is.... you are connected to Sales Force.

vendredi 2 septembre 2011

C# MongoDb - the really basics

I came from http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial#CSharpDriverTutorial-InsertBatchmethod, where I spent a little hour.

I found that a more explicit sample was missing. So here it is the C# version of the article.

The real question I'm trying to answer is : how to map a POCO to MongoDb.

using System;

using MongoDB.Driver;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;

namespace mdbBacASable {
  class Program {
    static void Main(String[] args) {
      try {
        String connectionString = 
            "mongodb://localhost:27017";
        MongoServer mdbServer = 
            MongoServer.Create(connectionString);
        MongoDatabase mdbTest = 
            mdbServer.GetDatabase("test");
        MongoCollection<BsonDocument> bsDocBooks = 
          mdbTest.GetCollection<BsonDocument>("books");
        bsDocBooks.RemoveAll();
        BsonDocument bsDocBook = new BsonDocument {
          { "Author", "Ernest Hemingway" },
          { "Title", "For Whom the Bell Tolls" }
        };
        
        bsDocBooks.Insert(bsDocBook);
        BsonDocument[] batch = {
          new BsonDocument {
            { "Author", "Kurt Vonnegut" },
            { "Title", "Cat's Cradle" }
          },
          new BsonDocument {
            { "Author", "Kurt Vonnegut" },
            { "Title", "Slaughterhouse-Five" }
          }
        };
        bsDocBooks.InsertBatch(batch);
          
        //-----------------------------------------------
        
        BsonClassMap.RegisterClassMap<Book>(cm => {
          cm.AutoMap();
          cm.SetIdMember(cm.GetMemberMap(c => c.Id));
        });
        MongoCollection<Book> Books = 
            mdbTest.GetCollection<Book>("books");
        Book oBook = new Book() { 
          Author = "JRR Tolkien", 
          Title = "Lord of the Ring" };
        Books.Insert(oBook);

        //----------------------------------------------
        
        Book Book = Books.FindOne();
        Console.WriteLine("{0} : {1}", 
          Book.Author, Book.Title);

        Console.WriteLine("\r\n------------------------\r\n");

        foreach ( Book b in Books.FindAll() ) {
          Console.WriteLine("{0} : {1} : {2}", 
            b.Id, b.Author, b.Title);
        }
      } catch (Exception ex) {
        Console.WriteLine(ex.Message);
      }
    }
  }

  public class Book {
    public ObjectId Id { get; set; }
    public String Author { get; set; }
    public String Title { get; set; }
  }
}

dimanche 28 août 2011

Managing default values for entities in EF: the from birth way

I recently face the problem of setting default value for entities when using entity framework.

After some walk around, here is a part of my actual solution.

This is not really the default values of the properties, but the default values of the types of the properties.

Please consider the following class:

using System;
using System.Linq;
using System.Reflection;


namespace AppXYZ.Entities {
    public static class EntityTools {
        public static readonly DateTime dtDef = new DateTime(1900, 1, 1, 0, 0, 0);

        public static void SetDefaults (object o) {
            Type T = o.GetType();
            foreach ( MemberInfo m in T.GetProperties() ) {
                PropertyInfo P = T.GetProperty(m.Name);
                switch ( Type.GetTypeCode(P.PropertyType) ) {
                    case TypeCode.String :
                        if ( P.GetValue(o, null) == null ) 
                            P.SetValue(o, String.Empty, null); 
                        break;
                    case TypeCode.DateTime :
                        if ( (DateTime)P.GetValue(o, null) == DateTime.MinValue )
                            P.SetValue(o, EntityTools.dtDef, null); 
                        break;
                }
            }
        }

        //T must have a constructor with 0 argument
        public static T GetNewEntity<T> () {
            T e;
            try {
                e = Activator.CreateInstance<T>();
            } catch {
                e = default(T);
            }
            SetDefaults(e);


            return e;
        }
    }
}

Since, I discover at least another way: the just on time way

mercredi 17 août 2011

Entity Framework: Code-First, Foreign Key One-To-Many

The object of this article is to (try to) illustrate two ways for configuring a one to many relation by using entity framework 4.1. For the need of the sample I use a very small and anecdotic part of the data model of Cegid Business Applications. That is I use code first on an existing database !!! :)

To help understanding differences, I comment code from one version to the other.

Of course you will need a connecion string:




Please note:

  • the activation of MARS

First: by using the Data Annotations

namespace ef {
    class Program {
        static void Main (string[] args) {
             CegidContext cc = new CegidContext();
            foreach ( gThirdParty gtp in (from t in cc.gThirdParties where t.GBusinesses.Count() > 0 select t) ) {
                 Console.WriteLine("{0,-17} : {1}", gtp.T_TIERS, gtp.GBusinesses.Count());
            }
        }
    }

    [Table("AFFAIRE")]
    public class gBusiness {
        [Key]
         public string AFF_AFFAIRE {get; set;}
         public string AFF_LIBELLE { get; set; }
         public string AFF_TIERS { get; set; }
 
        [ForeignKey("AFF_TIERS")]
         public virtual gThirdParty GThirdParty { get; set; }
    }

    [Table("TIERS")]
    public class gThirdParty {
         public gThirdParty () { 
            GBusinesses = new List<gBusiness>();
        }

        [Key]
         public string T_TIERS { get; set; }
         public string T_LIBELLE { get; set; }
         public virtual ICollection<gBusiness> GBusinesses { get; set; }
    }


    /*public class gBusinessConfiguration
        : EntityTypeConfiguration<gBusiness> {
        public gBusinessConfiguration ()
            : base() {
            HasKey(p => p.AFF_AFFAIRE);
            HasRequired(p => p.GThirdParty).WithMany(t => t.GBusinesses).Map(x => x.MapKey("AFF_TIERS")); 
        }
    }

    public class gThirdPartyConfiguration
        : EntityTypeConfiguration<gThirdParty> {
        public gThirdPartyConfiguration ()
            : base() {
            HasKey(p => p.T_TIERS);
        }
    }*/    
    
    public class CegidContext : DbContext {
        public DbSet<gBusiness> gBussinesses { get; set; }
        public DbSet<gThirdParty> gThirdParties { get; set; }

        /*protected override void OnModelCreating (DbModelBuilder modelBuilder) {
            modelBuilder.Configurations.Add(new gBusinessConfiguration());
            modelBuilder.Configurations.Add(new gThirdPartyConfiguration());
            base.OnModelCreating(modelBuilder);
        }*/
    }
}

Second: by using the fluent API

namespace ef { 
    class Program { 
        static void Main (string[] args) {
            CegidContext cc = new CegidContext();
            foreach ( gThirdParty gtp in (from t in cc.gThirdParties where t.GBusinesses.Count() > 0 select t) ) {
                 Console.WriteLine("{0,-17} : {1}", gtp.T_TIERS, gtp.GBusinesses.Count());
            }
        }
    }

    [Table("AFFAIRE")]
    public class gBusiness { 
        //[Key] 
         public string AFF_AFFAIRE {get; set;} 
         public string AFF_LIBELLE { get; set; } 
         //public string AFF_TIERS { get; set; } 
              
        //[ForeignKey("AFF_TIERS")] 
         public virtual gThirdParty GThirdParty { get; set; } 
    }

    [Table("TIERS")] 
     public class gThirdParty { 
         public gThirdParty () { 
            GBusinesses = new List<gBusiness>(); 
        }

        //[Key] 
         public string T_TIERS { get; set; } 
         public string T_LIBELLE { get; set; } 
         public virtual ICollection<gBusiness> GBusinesses { get; set; } 
    }

    public class gBusinessConfiguration 
       : EntityTypeConfiguration<gBusiness> { 
        public gBusinessConfiguration () 
           : base() { 
           HasKey(p => p.AFF_AFFAIRE); 
           HasRequired(p => p.GThirdParty).
               WithMany(t => t.GBusinesses).
               Map(x => x.MapKey("AFF_TIERS")); 
       } 
    }

    public class gThirdPartyConfiguration 
        : EntityTypeConfiguration<gThirdParty> { 
         public gThirdPartyConfiguration () 
            : base() { 
            HasKey(p => p.T_TIERS); 
        } 
    }

    public class CegidContext : DbContext { 
         public DbSet<gBusiness> gBussinesses { get; set; } 
         public DbSet<gThirdParty> gThirdParties { get; set; }
 
         protected override void OnModelCreating (DbModelBuilder modelBuilder) { 
            modelBuilder.Configurations.Add(new gBusinessConfiguration()); 
            modelBuilder.Configurations.Add(new gThirdPartyConfiguration()); 
            base.OnModelCreating(modelBuilder); 
        } 
    } 
}

Enjoy

Pending question:

  • How to avoid lazy loading and force explicit loading as Linq to SQL allows with DataLoadOption in the DataContext.

Edit -----


About not lazy, here is the way: cc.gThirdParties.Include("GBusinesses")

lundi 1 août 2011

Ninject by the sample I

Here you will find 2 samples that I wrote to help me materializing the "magic" of Ninject and more generally of DI.

using System;

using Ninject;
using Ninject.Modules;

namespace simplInject {

class Program {
    static void Main (string[] args) {
        IKernel ik = new StandardKernel(new imod());
                  
        //constructor with 2 interfaces.
        I3 c = ik.Get<I3>(); // will "magically" instanciate C1 and C2 instances from the binding
        Console.WriteLine("{0} with {1} C1 instance", c.concat, C1.cpt);
        //constructor with 1 interface and 1 parameter.
        I4 c4 = ik.Get<I4>(); // // will "magically" instanciate another C1 instance from the binding
        Console.WriteLine("{0} with {1} C1 instance", c4.concat, C1.cpt);
    }
}

class imod : NinjectModule {
    public override void Load () {
        //Here the WithconstructorArgument is to trace the binding in the main method.
        Bind<I1>().To<C1>().WithConstructorArgument("s", "string I");
        Bind<I2>().To<C2>().WithConstructorArgument("s", "string II");
        Bind<I3>().To<C3>();
        Bind<I4>().To<C4>().WithConstructorArgument("s", "string III");
   }

}

public interface I1 {
    string s1 { get; set; }
}
public interface I2 {
    string s2 { get; set; }
}
public interface I3 {
    string concat { get; }
}
public interface I4 {
     string concat { get; }
}

public class C1 : I1 {
    public static int cpt = 0;
    private int _id;
    string _s;
          
    public C1 (string s) {
        _id = C1.cpt++;
        _s = s;
    }

    public int Id { get { return _id; } }
 
    public string s1 {
        get { return this._s; }
        set { this._s = value; }
    }
}

public class C2 : I2 {
    string _s;

    public C2 (string s) {
        this._s = s;
    }

    public string s2 {
        get { return this._s; }
        set { this._s = value; }
    }
}

public class C3 : I3 {
    I1 _c1; I2 _c2;
          
    public C3(I1 c1, I2 c2) {
        _c1 = c1; _c2 = c2;
    }

    public string concat {
        get { return _c1.s1 + " " + _c2.s2; }
    }
}

public class C4 : I4 {
    I1 _c1; string _s;

    public C4 (string s, I1 c1) {
        _c1 = c1;
        _s = s;
    }

    public string concat {
        get { return _s + " " + _c1.s1; }
    }
}

}