Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Layers Pattern in Practice

, 23 Apr 2010 CPOL
Layers Pattern via a WPF project.
AdditionalDependencies.zip
WPFVisifire.Charts.dll
AvalonControlsLibrary.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
WPFToolkit.dll
BillsManager.zip
BillsManager
BillManagerUninstallAction
Properties
BillPayManager
Application.ico
BillPayManager.cd
BillPayManager.csproj.user
bin
Debug
AvalonControlsLibrary.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
WPFToolkit.dll
WPFVisifire.Charts.dll
Charting
DataGrid
icon.ico
Images
about.png
archive.png
backArrow.png
bills.png
charts.png
fwdArrow.png
icon.ico
settings.png
Properties
Themes
BillPayManager.suo
BillsBusinessLogicLib
BillsBusinessLogicLib.csproj.user
BLLBills.cd
ciumac.sergiu.pfx
Properties
BillsDalLib
BillsDalClassDiagram.cd
BillsDalLib.csproj.user
ciumac.sergiu.pfx
DALSchema
Properties
BillsEntityLib
BillEntityClassDiagram.cd
BillsEntityLib.csproj.user
ciumac.sergiu.pfx
Properties
SetupBillPayManager
SetupBillPayManager.vdproj
TestBillPayManager
Properties
TestBillPayManager.csproj.user
TestBillPayManagerManual
AddBillManualTest.mht
DeleteBill.mht
Properties
Release.zip
AvalonControlsLibrary.dll
BillPayManager.exe
BillPayManager.vshost.exe
BillsBusinessLogicLib.dll
BillsDalLib.dll
BillsEntityLib.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
WPFToolkit.dll
WPFVisifire.Charts.dll
en-US
BillPayManager.resources.dll
ro-RO
BillPayManager.resources.dll
ReleaseSetup.zip
SetupBillPayManager.msi
setup.exe
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Diagnostics;
using System.Security.Permissions;
using BillsEntityLib;
using System.Threading;
using System.Globalization;

namespace BillsDalLib
{
    /// <summary>
    /// XML data source manager
    /// </summary>
    public class XmlDalBillsManager : IDalBillsManager
    {
        private static XmlDalBillsManager _manager = null;
        private string _archive;
        private static object lockObject = new object();
        private static BooleanSwitch _bSwitch;

        private static readonly Semaphore Turnstyle = new Semaphore(1, 65535);
        private static readonly Semaphore RoomEmpty = new Semaphore(1, 1);
        private static int _totalreaders = 0;
        private static readonly object ReadersLock = new object();

        /// <summary>
        /// Constants for Xml file. The same are used by XmlSerializer class. Any changes made here
        /// can prevent the deserialization of Xml storage file.
        /// </summary>
        #region XML constants

        private const string XSI = "http://www.w3.org/2001/XMLSchema-instance"; //xml schema instance
        private const string XSD = "http://www.w3.org/2001/XMLSchema";  //xml schema
        private static readonly XNamespace SCHEMA = "http://tempuri.org/billschema.xsd"; //local xml schema

        private readonly XName BILLS = SCHEMA + "Bills"; //<Bills>
        private readonly XName BILL = SCHEMA + "Bill";       //<Bill>
        private readonly XName NAME = SCHEMA + "Name";           //<Name>  
        private readonly XName DUEDATE = SCHEMA + "DueDate";     //<DueDate>
        private readonly XName AMOUNT = SCHEMA + "Amount";       //<Amount>
        private readonly XName ADDEDON = SCHEMA + "AddedOn";     //<AddedOn>
        private readonly XName STATUS = SCHEMA + "Status";       //<Status>
        private readonly XName ID = "ID";    //<Bill ID = "0000-0000-00000000"/>
        #endregion

        #region Constructor
        private XmlDalBillsManager()
        {
            _bSwitch = new BooleanSwitch("BooleanSwitch", "Switch from config file"); //if no such switch in config file _bSwitch == false
            if(_bSwitch.Enabled)
                Trace.TraceInformation("Singleton Instance Created:"+this.GetType().ToString());
        }
        /// <summary>
        /// Private constructor
        /// </summary>
        /// <param name="archive">Path to archive directory [path to directory]</param>
        private XmlDalBillsManager(string archive) : this()
        {
            archive = Path.GetFullPath(archive);
            if (String.IsNullOrEmpty(archive))
            {
                CreateArchiveFile(archive);
            }
            this._archive = archive;
        }
        #endregion
        #region Private members
        /// <summary>
        /// Get data archive
        /// </summary>
        /// <returns>Array of paths to data archive files</returns>
        private XDocument GetArchive()
        {

            if (String.IsNullOrEmpty(_archive))
                throw new DalBillManagerException("Path to archive is null");
            if (!File.Exists(_archive))
                return CreateArchiveFile(_archive);
            XDocument ret = null;
            try
            {
                ret = XDocument.Load(_archive);
            }
            catch(Exception ex)
            {
                throw new DalBillManagerException(ex.Message);
            }
            return ret;
        }
        /// <summary>
        /// Creates new Xml document in data storage location
        /// </summary>
        /// <param name="fileName">Filename to create</param>
        /// <returns>XDocument instance</returns>
        /// <exception cref="System.Security.SecurityException">Security exception</exception>
        private XDocument CreateArchiveFile(string fileName)
        {
            fileName = Path.GetFullPath(fileName);
            if (String.IsNullOrEmpty(fileName))
                throw new DalBillManagerException("Path to archive is null");
            FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Write, Path.GetFullPath(fileName));
            try
            {
                permission.Demand();    //demand for file creation
            }
            catch(System.Security.SecurityException e)
            {
                if(_bSwitch.Enabled)
                    Trace.TraceError("SecurityException - Message:"+e.Message+"\nStackTrace:"+e.StackTrace);
                throw;
            }
           
            XDocument xmlDoc = new XDocument(new XDeclaration("1.0", "UTF-8", null),    //Create Root element
                new XElement(BILLS));
            if (xmlDoc.Root != null)
                xmlDoc.Root.Add(
                    new XAttribute(XNamespace.Xmlns + "xsi", XSI),
                    new XAttribute(XNamespace.Xmlns + "xsd", XSD));
            try
            {
                xmlDoc.Save(fileName);
            }
            catch (Exception)
            {
                if (_bSwitch.Enabled)
                    Trace.TraceError("Archive wasnt created:" + fileName);
                throw new DalBillManagerException("Cannot save Xml file. Review the path");
            }
            if (_bSwitch.Enabled)
                Trace.TraceInformation("Archive created:" + fileName);
            return xmlDoc;
        }
        #endregion
        #region Singleton
        /// <summary>
        /// Gets singleton instance of XmlDalBillsManager class
        /// </summary>
        /// <returns>XmlDalBillsManager instance</returns>
        public static XmlDalBillsManager GetInstance()
        {
            if (_manager == null)   //lazy initialization
            {
                lock (lockObject)
                {
                    if (_manager == null)
                    {
                        _manager = new XmlDalBillsManager();
                    }
                }
            }
            return _manager;
        }
        #endregion

        #region IDalBillsManager Members
        /// <summary>
        /// Reads bills from the data source. Method is thread-safe.
        /// </summary>
        /// <param name="fromDate">Most earliest date</param>
        /// <param name="toDate">Most latest date</param>
        /// <returns>List of bills</returns>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public Bills Read(DateTime fromDate, DateTime toDate)
        {
            Turnstyle.WaitOne();
            Turnstyle.Release();
            lock (ReadersLock)
            {
                _totalreaders += 1;
                if (_totalreaders == 1)
                    RoomEmpty.WaitOne();
            }
            XDocument xmlDoc = null;
            try
            {
                xmlDoc = this.GetArchive();
            }
            catch
            {
                lock (ReadersLock)
                {
                    _totalreaders--;
                    if (_totalreaders == 0)
                        RoomEmpty.Release();
                }
                throw;
            }

            List<Bill> listOfBills = new List<Bill>();
            var query = from result in xmlDoc.Elements(BILLS).Elements(BILL)
                        where Convert.ToDateTime(result.Element(ADDEDON).Value, CultureInfo.InvariantCulture).CompareTo(fromDate) > 0
                              && Convert.ToDateTime(result.Element(ADDEDON).Value, CultureInfo.InvariantCulture).CompareTo(toDate) < 0
                        select result;
            List<XElement> list = query.ToList();
            foreach (XElement record in list)
            {
                if (record != null)
                {
                    string name = record.Element(NAME).Value;
                    DateTime dueDate = Convert.ToDateTime(record.Element(DUEDATE).Value, CultureInfo.InvariantCulture);
                    decimal amount = Convert.ToDecimal(record.Element(AMOUNT).Value, CultureInfo.InvariantCulture);
                    DateTime addedOn = Convert.ToDateTime(record.Element(ADDEDON).Value, CultureInfo.InvariantCulture);
                    BillStatus status = BillHelper.Convert(record.Element(STATUS).Value);
                    Guid id = new Guid(record.Attribute(ID).Value);
                    listOfBills.Add(new Bill(name, dueDate, amount, addedOn, status, id));
                }
            }

            lock (ReadersLock)
            {
                _totalreaders--;
                if (_totalreaders == 0)
                    RoomEmpty.Release();
            }

            return new Bills(listOfBills);
        }
        /// <summary>
        /// Read a bill from the datasource by its ID. Method is thread-safe.
        /// </summary>
        /// <param name="guid">Bill's guid</param>
        /// <returns>Bill object or null if no such object in the datasource</returns>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public Bill ReadById(Guid guid)
        {
            if (guid == Guid.Empty)
                return null;

            Turnstyle.WaitOne();
            Turnstyle.Release();
            lock (ReadersLock)
            {
                _totalreaders += 1;
                if (_totalreaders == 1)
                    RoomEmpty.WaitOne();
            }
            XDocument xmlDoc = null;
            try
            {
                xmlDoc = this.GetArchive();
            }
            catch
            {
                lock (ReadersLock)
                {
                    _totalreaders--;
                    if (_totalreaders == 0)
                        RoomEmpty.Release();
                }
                throw;
            }
            string idStr = guid.ToString();
            var query = from record in xmlDoc.Elements(BILLS).Elements(BILL)
                        where (string)record.Attribute(ID) == idStr
                        select record;
            List<XElement> list = query.ToList();
            if (list.Count() == 0)
            {
                lock (ReadersLock)
                {
                    _totalreaders--;
                    if (_totalreaders == 0)
                        RoomEmpty.Release();
                }
                return null;
            }
            XElement bill = list.ElementAt(0);
            if (bill != null)
            {
                string name = bill.Element(NAME).Value;
                DateTime dueDate = Convert.ToDateTime(bill.Element(DUEDATE).Value, CultureInfo.InvariantCulture);
                decimal amount = Convert.ToDecimal(bill.Element(AMOUNT).Value, CultureInfo.InvariantCulture);
                DateTime addedOn = Convert.ToDateTime(bill.Element(ADDEDON).Value, CultureInfo.InvariantCulture);
                BillStatus status = BillHelper.Convert(bill.Element(STATUS).Value);
                Guid id = new Guid(bill.Attribute(ID).Value);

                lock (ReadersLock)
                {
                    _totalreaders--;
                    if (_totalreaders == 0)
                        RoomEmpty.Release();
                }

                return new Bill(name, dueDate, amount, addedOn, status, id);
            }
            else
                return null;
        }
        /// <summary>
        /// Insert bills into Xml. Method is thread-safe.
        /// </summary>
        /// <param name="bills">Bill to insert</param>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public void Insert(Bills bills)
        {
            if (bills == null || bills.Items == null)
                return;
            Turnstyle.WaitOne();
            RoomEmpty.WaitOne();
            try
            {
                XDocument xmlDoc = this.GetArchive();
                XElement current = xmlDoc.Root;
                foreach (Bill bill in bills.ToList())
                {
                    XElement toAdd = new XElement(BILL, new XAttribute(ID, bill.ID),
                                                  new XElement(NAME, bill.Name),
                                                  new XElement(DUEDATE, bill.DueDate),
                                                  new XElement(AMOUNT, bill.Amount),
                                                  new XElement(ADDEDON, bill.AddedOn),
                                                  new XElement(STATUS, bill.Status));
                    if (current != null) current.Add(toAdd);
                    if (_bSwitch.Enabled)
                        Trace.TraceInformation("New element inserted:" + bill.ID.ToString());
                }
                xmlDoc.Save(_archive);
            }
            catch
            {
                throw;
            }
            finally
            {
                Turnstyle.Release();
                RoomEmpty.Release();
            }
        }
        /// <summary>
        /// Delete a bill from list of recent bills. Method is thread-safe.
        /// </summary>
        /// <param name="guid">Delete a bill with the specified Guid.</param>
        /// <returns>Number of deleted items</returns>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public int Delete(Guid guid)
        {
            if (guid == Guid.Empty)
                return 0;

            Turnstyle.WaitOne();
            RoomEmpty.WaitOne();
            int count = 0;
            try
            {
                XDocument xmlDoc = this.GetArchive();
                string strBill = guid.ToString();
                var query = from bills in xmlDoc.Elements(BILLS).Elements(BILL)
                            where (string)bills.Attribute(ID) == strBill
                            //select bills with defined ID
                            select bills;
                count = query.Count();
                if (count != 0)
                {
                    List<XElement> list = query.ToList();
                    foreach (XElement record in list)
                        record.Remove();
                }
                xmlDoc.Save(_archive); //save the file
            }
            catch
            {
                throw;
            }
            finally
            {
                Turnstyle.Release();
                RoomEmpty.Release();
            }
            return count;
        }
        /// <summary>
        /// Delete a list of specified Bills from the datasource. Method is thread-safe.
        /// </summary>
        /// <param name="guids">Guids of bills to delete</param>
        /// <returns>Number of deleted items</returns>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public int Delete(IEnumerable<Guid> guids)
        {
            Turnstyle.WaitOne();
            RoomEmpty.WaitOne();
            int totalCount = 0;

            try
            {
                XDocument xmlDoc = this.GetArchive();              
                foreach (Guid id in guids)
                {
                    string strBill = id.ToString();
                    var query = from bills in xmlDoc.Elements(BILLS).Elements(BILL)
                                where (string)bills.Attribute(ID) == strBill
                                //select bills with defined ID
                                select bills;
                    totalCount += query.Count();
                    if (query.Count() != 0)
                    {
                        List<XElement> list = query.ToList();
                        foreach (XElement record in list)
                            record.Remove();
                    }
                }
                xmlDoc.Save(_archive); //save the file
            }
            catch
            {
                throw;
            }
            finally
            {
                Turnstyle.Release();
                RoomEmpty.Release();
            }

            return totalCount;
        }
        /// <summary>
        /// Update information about the bill. Method is thread-safe.
        /// </summary>
        /// <param name="billId">Bill Id</param>
        /// <param name="newBill">New bill information</param>
        /// <returns>Number of updated items</returns>
        /// <remarks>Written at 06.11.2009. Thread-safety defined using Readers/Writers pattern</remarks>
        public int Update(Guid billId, Bill newBill)
        {
            if (billId == Guid.Empty || newBill == null)
                return 0;

            Turnstyle.WaitOne();
            RoomEmpty.WaitOne();
            int totalCount = 0;
            try
            {
                XDocument xmlDoc = this.GetArchive();
                string billstr = billId.ToString();
                var query = from record in xmlDoc.Elements(BILLS).Elements(BILL)
                            where (string)record.Attribute(ID) == billstr
                            select record;

                totalCount = query.Count();
                if (totalCount != 0)
                {
                    List<XElement> list = query.ToList();
                    foreach (XElement record in list)
                    {
                        record.SetElementValue(NAME, newBill.Name);
                        record.SetElementValue(DUEDATE, newBill.DueDate);
                        record.SetElementValue(AMOUNT, newBill.Amount);
                        record.SetElementValue(ADDEDON, newBill.AddedOn);
                        record.SetElementValue(STATUS, newBill.Status);
                        if (_bSwitch.Enabled)
                            Trace.TraceInformation("Element Updated:" + billId.ToString());
                    }
                }
                xmlDoc.Save(_archive);
            }
            catch
            {
                throw;
            }
            finally
            {
                Turnstyle.Release();
                RoomEmpty.Release();
            }
            return totalCount;
        }
        /// <summary>
        /// Settings for the XmlDalBillsManager instance. Supports only 1 setting - [string]:archive path.
        /// </summary>
        public object[] Settings
        {
            get { return new object[2] { _archive, _bSwitch.Enabled }; }
            set 
            {
                if (value.Length != 2 || value.Length == 0 || (value[0].GetType() != typeof(string) && value[1].GetType() != typeof(bool)))
                    throw new DalBillManagerException("Wrong settings");
                string prevValue = _archive;
                _archive = Path.GetFullPath((string)value[0]);
                
                _bSwitch.Enabled = (bool)value[1];
                if (!File.Exists(_archive))
                {
                    this.CreateArchiveFile(_archive);
                }
                try
                {
                    /*Try Loading file*/
                    this.GetArchive();
                }
                catch (DalBillManagerException)
                {
                    _archive = prevValue;
                    throw;
                }
            }
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ciumac Sergiu
Software Developer
Moldova (Republic Of) Moldova (Republic Of)
Interested in computer science, math, research, and everything that relates to innovation. Fan of agnostic programming, don't mind developing under any platform/framework if it explores interesting topics. In search of a better programming paradigm.
Follow on   Twitter   LinkedIn

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 23 Apr 2010
Article Copyright 2010 by Ciumac Sergiu
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid