WPF object model control with singleton and static messenger? ConditionalWeakTable?

As always first I have to say that I'm an amateur and not native English speaker, so please have a little patience if I write nonsense ;)

I've worked as an accountant for more than 10 years and since like one year I'm working as a freelancer accountant, so I'm trying to make an accounting app for my personal use (well, lately I wonder this, since the app is getting really big), using c# and WPF.

The thing is that I'm trying to achieve a chrome alike tab-based GUI, with lots of object models that can be duplicated across the different tabs, something that I don't want to happen since some objects can be BIG. So, googling and reading StackOverflow I've come to think in a sort of singleton that store all the object models, which can be asked for any single object. Since I have several assemblies in my solution, I can not access that singleton from any part of the app, so I made a static messenger located in an assembly that is referenced by all the other assemblies (it's called GeneralHelpers), that launch events to which the singleton, located at the only assembly that references all the other assemblies(called AdConta), is subscribed.

The singleton, note that the type switch is not finished, since I'm not sure of some things yet (that's why I'm here ;D I'm not even sure if this is a real singleton or just something else). At the end the type switches should include all the object models used by the app:

namespace AdConta.ModelControl {     /// <summary>     /// Control and store ALL object models. Just for internal use, to add object models use static AppModelControlMessenger.     /// </summary>     public class AppModelControl     {         public AppModelControl()         {             AppModelControlMessenger.ModelAddedEvent += OnModelAddedEvent;             AppModelControlMessenger.ObjModelAskedEvent += OnObjModelAskedEvent;              this._Comunidades = new Dictionary<int, Comunidad>();             this._Personas = new Dictionary<int, Persona>();             this._Conceptos = new Dictionary<int, Concepto>();         }          #region fields         private Dictionary<int, Comunidad> _Comunidades;         private Dictionary<int, Persona> _Personas;         private Dictionary<int, Concepto> _Conceptos;         #endregion          #region public methods         public void UnsubscribeModelControlEvents()         {             AppModelControlMessenger.ModelAddedEvent -= OnModelAddedEvent;             AppModelControlMessenger.ObjModelAskedEvent -= OnObjModelAskedEvent;         }         #endregion          #region events         /// <summary>         /// Add object e.Model to the corresponding dictionary, WITHOUT checking if owners exists. The model have to be asked first with          /// AppModelControlMessenger.AskForModel         /// </summary>         /// <param name="sender"></param>         /// <param name="e"></param>         private void OnModelAddedEvent(object sender, ModelControlEventArgs e)         {             //TypeSwitch.Case<>(x=>)             TypeSwitch.Do(e.ObjectModel,                 TypeSwitch.Case<Comunidad>(x =>                  {                     Comunidad model = (Comunidad)e.ObjectModel;                     this._Comunidades.Add(model.Id, model);                 }),                 TypeSwitch.Case<Persona>(x =>                  {                     Persona model = (Persona)e.ObjectModel;                     this._Personas.Add(model.Id, model);                 }),                 TypeSwitch.Case<Concepto>(x =>                  {                     Concepto model = (Concepto)e.ObjectModel;                     this._Conceptos.Add(model.Id, model);                 })     #if (MGESTION)                 ,                 TypeSwitch.Case<ComunidadGestion>(x =>                 {                     ComunidadGestion model = (ComunidadGestion)e.ObjectModel;                      this._Comunidades[model.IdOwnerComunidad].SetCdadGestion(ref model);                 }),                 TypeSwitch.Case<Finca>(x =>                 {                     Finca model = (Finca)e.ObjectModel;                      this._Comunidades[model.IdOwnerComunidad]._Fincas.Add(model.Id, model);                 }),                 /*TypeSwitch.Case<Cuota>(x =>                 {                     Cuota model = (Cuota)e.Model;                      this._Comunidades[model.OwnerIdCdad]._Fincas[model.OwnerIdFinca].Cuotas.Add(model.Id, model);                 }),*/                 TypeSwitch.Case<Recibo>(x =>                 {                  })     #endif     #if (MCONTABILIDAD)                 ,                 TypeSwitch.Case<ComunidadContabilidad>(x =>                 {                     ComunidadContabilidad model = (ComunidadContabilidad)e.ObjectModel;                      this._Comunidades[model.IdOwnerComunidad].SetCdadContabilidad(ref model);                 })     #endif             );         }         private void OnObjModelAskedEvent(ref object sender, ModelControlEventArgs e)         {             object objModel = null;             int id;              /*TypeSwitch.Do(e.ObjectModel,                 TypeSwitch.Case<Comunidad>(x => ModelExists = this._Comunidades.ContainsKey(((Comunidad)e.ObjectModel).Id)),                 TypeSwitch.Case<Persona>(x => ModelExists = this._Personas.ContainsKey(((Persona)e.ObjectModel).Id)),                 TypeSwitch.Case<Concepto>(x => ModelExists = this._Conceptos.ContainsKey(((Concepto)e.ObjectModel).Id))                 );*/              TypeSwitch.Do(e.ObjectModel,                 TypeSwitch.Case<Comunidad>(x =>                 {                     id = ((Comunidad)e.ObjectModel).Id;                     if (this._Comunidades.ContainsKey(id)) objModel = this._Comunidades[id];                     else objModel = null;                 }),                 TypeSwitch.Case<Persona>(x =>                  {                     id = ((Persona)e.ObjectModel).Id;                     if (this._Personas.ContainsKey(id)) objModel = this._Personas[id];                     else objModel = null;                 }),                 TypeSwitch.Case<Concepto>(x =>                 {                     id = ((Concepto)e.ObjectModel).Id;                     if (this._Conceptos.ContainsKey(id)) objModel = this._Conceptos[id];                     else objModel = null;                 })     #if (MGESTION)                 ,                 TypeSwitch.Case<ComunidadGestion>(x =>                 {                     id = ((ComunidadGestion)e.ObjectModel).IdOwnerComunidad;                     if (this._Comunidades.ContainsKey(id) && this._Comunidades[id].CdadGestion.IdOwnerComunidad == id)                         objModel = this._Comunidades[id].CdadGestion;                     else objModel = null;                 })     #endif     #if (MCONTABILIDAD)                 ,                 TypeSwitch.Case<ComunidadContabilidad>(x =>                 {                     id = ((ComunidadContabilidad)e.ObjectModel).IdOwnerComunidad;                     if (this._Comunidades.ContainsKey(id) && this._Comunidades[id].CdadContabilidad.IdOwnerComunidad == id)                         objModel = this._Comunidades[id].CdadContabilidad;                     else objModel = null;                 })     #endif                 );              AppModelControlMessenger.SetMsgFromAppModelcontrol(ref sender, ref objModel);         }         #endregion     }  } 

It have only three dictionaries. To shorten it, all my object models are "contained" as properties by one object of the types Persona, Comunidad or Concepto. Also I've just "categorized" all my object models with interfaces, so I can recognize which type of object contains it and each object have an ID property given by the database which I use as a key, forced by the corresponding interface. As a side note, I "stole" the TypeSwitch class directly from this stackoverflow question.

The messenger:

namespace AdConta.ModelControl {     /// <summary>     /// Static class for adding objectModels so they don't get duplicated.     /// Use AppModelControlMessenger.AskForObjModel(ref object sender, ref object objModel)     /// </summary>     public static class AppModelControlMessenger     {         #region fields         /// <summary>         /// key = sender, value = objModelAsked         /// </summary>         private static Dictionary<object, object> _MsgDict = new Dictionary<object, object>();         #endregion          #region events         public delegate void ModelAddedEventHandler(object sender, ModelControlEventArgs e);         public static event ModelAddedEventHandler ModelAddedEvent = delegate { };         /// <summary>         /// Add object Model to the corresponding dictionary, WITHOUT checking if owners exists. The model have to be asked first with         /// AppModelControlMessenger.AskForModel         /// </summary>         /// <param name="objModel"></param>         internal static void AddModel(ref object objModel)         {             ModelControlEventArgs e = new ModelControlEventArgs(ref objModel);              ModelAddedEvent(null, e);         }          public delegate void ObjModelAskedEventHandler(ref object sender, ModelControlEventArgs e);         public static event ObjModelAskedEventHandler ObjModelAskedEvent = delegate { };         /// <summary>         /// Ask AppModelControl if objModel exists. Return true if objModel exists(and vv) and, if objModel exists, it assign objModel via          /// ref parameter to the existing object (objModel = existingObject)         /// </summary>         /// <param name="sender"></param>         /// <param name="objModel"></param>         /// <returns></returns>         public static bool AskForObjModel(ref object sender, ref object objModel)         {             ModelControlEventArgs e = new ModelControlEventArgs(ref objModel);              if (!_MsgDict.ContainsKey(sender)) _MsgDict.Add(sender, null);             ObjModelAskedEvent(ref sender, e);              if (_MsgDict[sender] == null)             {                 _MsgDict.Remove(sender);                 AddModel(ref objModel);                 return false;             }              objModel = _MsgDict[sender];             _MsgDict.Remove(sender);             return true;              /*bool ret =(_MsgDict[sender] == null ? false : (bool)_MsgDict[sender]);             _MsgDict.Remove(sender);             return ret;*/         }         #endregion          #region public methods         /// <summary>         /// For internal use only. Don't use it unless you are modifying AppModelControl class.         /// Set result the of a ModelAskedEvent ing the messages static dictionary.         /// </summary>         /// <param name="key"></param>         /// <param name="objModel"></param>         public static void SetMsgFromAppModelcontrol(ref object key, ref object objModel)         {             if (!_MsgDict.ContainsKey(key)) return;              _MsgDict[key] = objModel;         }         #endregion     }      public class ModelControlEventArgs : EventArgs     {         public ModelControlEventArgs(ref object objectModel)         {             this._ObjectModel = objectModel;         }          private object _ObjectModel;         public object ObjectModel { get { return this._ObjectModel; } }     } } 

It works basically with the method AskForModel, that method launch the ObjModelAskedEvent. I use that method from the APP so it "asks" AppModelControl if the object model already exists, which makes whatever needed in the private message dictionary (if the object exists, store the object as the value of sender key, if it doesn't exist store null as value). So, I just have to do something like this at any part of the app:

    Person person = new Person();     AppModelControlMessenger.AskForModel(ref this, ref person);      //If the object exists, it's assigned to the existing one, if not,     nothing more happens     //... 

I create AppModelControl at the App.xaml.cs, and unsubscribe the events at the App OnExit event:

namespace AdConta {     /// <summary>     /// Interaction logic for App.xaml     /// </summary>     public partial class App : Application     {         public App()         {             InitializeComponent();              FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata             {                 DefaultValue = FindResource(typeof(Window))             });              this._AppModelControl = new ModelControl.AppModelControl();         }          private ModelControl.AppModelControl _AppModelControl;          protected override void OnStartup(StartupEventArgs e)         {             FrameworkElement.LanguageProperty.OverrideMetadata(                 typeof(FrameworkElement),                 new FrameworkPropertyMetadata(                     System.Windows.Markup.XmlLanguage.GetLanguage(                     System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag)));             base.OnStartup(e);         }          protected override void OnExit(ExitEventArgs e)         {             this._AppModelControl.UnsubscribeModelControlEvents();             base.OnExit(e);         }     } } 

Now, the thing is that I was designing this thing without thinking in removing the objects later, now that I've implemented it, although it seems to work, I've realized that I have to remove all the objects directly from the dictionaries since the dictionaries keep a reference to them, so they don't get garbage collected until the app finish. After searching and reading I found the ConditionalWeakTable class, that seems to let GC to collect the keys stored.

So, the main question is:

Should I use that collection instead of the usual dictionaries, or am I doing something totally wrong with all this and I should rewrite it all? I feel really insecure about this thing although at a first glance, it seems to work. ANY constructive criticism would be really appreciated.


Category: c# Time: 2016-07-28 Views: 0
Tags: wpf

Related post

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development


Front-end development


development tools

Open Platform

Javascript development

.NET development

cloud computing


Copyright (C) avrocks.com, All Rights Reserved.

processed in 0.225 (s). 12 q(s)