/// <summary>
        /// Записывает индивидуальное сообщение лога для переданного объекта
        /// </summary>
        /// <param name="obj">Объект логирования</param>
        /// <param name="message">Сообщение лога</param>
        public static void f_CustomMessageLog(I_ELog obj, string message)
        {
            if (Cl_App.m_DataContext == null)
            {
                return;
            }

            Cl_ELogClassAttribute classAtr = Cl_EntityLog.f_GetClassAttribute <Cl_ELogClassAttribute>(obj);

            if (classAtr == null)
            {
                return;
            }

            Cl_Log outEvent = new Cl_Log
            {
                p_SessionID  = Cl_SessionFacade.f_GetInstance().p_SessionID,
                p_ElementID  = obj.p_GetLogEntityID,
                p_Version    = obj.p_Version,
                p_EntityType = classAtr.p_EntityType,
                p_ChangeTime = DateTime.Now,
                p_Event      = message,
                p_UserName   = Cl_SessionFacade.f_GetInstance().p_Doctor.p_FIO
            };

            Cl_App.m_DataContext.p_Logs.Add(outEvent);
            Cl_App.m_DataContext.SaveChanges();
        }
        /// <summary>
        /// Возвращает сформированный объект сообщения лога для текущего объекта
        /// </summary>
        private Cl_Log f_CreateEvent(I_ELog obj, string message)
        {
            Cl_Log outEvent = new Cl_Log
            {
                p_SessionID  = Cl_SessionFacade.f_GetInstance().p_SessionID,
                p_ElementID  = obj.p_GetLogEntityID,
                p_Version    = obj.p_Version,
                p_EntityType = this.entityLogType,
                p_ChangeTime = DateTime.Now,
                p_Event      = message,
                p_UserName   = Cl_SessionFacade.f_GetInstance().p_Doctor.p_FIO
            };

            return(outEvent);
        }
        /// <summary>
        /// Определяет имеются ли записи логирования у переданного объекта
        /// </summary>
        /// <returns>True - записей нет, False - записи имеются</returns>
        private bool f_IsNew(I_ELog obj)
        {
            Cl_ELogClassAttribute classAtr = Cl_EntityLog.f_GetClassAttribute <Cl_ELogClassAttribute>(obj);

            if (classAtr == null)
            {
                return(false);
            }

            Cl_Log prevEvent = null;

            if (obj.p_GetLogEntityID != 0)
            {
                prevEvent = Cl_App.m_DataContext.p_Logs.Where(l => l.p_ElementID == obj.p_GetLogEntityID && l.p_EntityType == classAtr.p_EntityType).OrderByDescending(d => d.p_ChangeTime).FirstOrDefault();
            }

            return(prevEvent == null);
        }
        /// <summary>
        /// Записывает индивидуальное сообщение лога для переданного объекта
        /// </summary>
        /// <param name="obj">Объект логирования</param>
        /// <param name="message">Сообщение лога</param>
        public static void f_CustomMessageLog(E_EntityTypes entityType, string message, int id = 1, int version = 0)
        {
            if (Cl_App.m_DataContext == null)
            {
                return;
            }

            Cl_Log outEvent = new Cl_Log
            {
                p_SessionID  = Cl_SessionFacade.f_GetInstance().p_SessionID,
                p_ElementID  = id,
                p_Version    = version,
                p_EntityType = entityType,
                p_ChangeTime = DateTime.Now,
                p_Event      = message,
                p_UserName   = Cl_SessionFacade.f_GetInstance().p_Doctor.p_FIO
            };

            Cl_App.m_DataContext.p_Logs.Add(outEvent);
            Cl_App.m_DataContext.SaveChanges();
        }
        /// <summary>
        /// Вызывается после сохранения элемента, что бы определить какие изменения были сделаны
        /// </summary>
        /// <param name="obj"></param>
        public void f_SaveEntity(I_ELog obj, string textNew = null)
        {
            if (obj == null)
            {
                return;
            }

            Cl_ELogClassAttribute classAtr = Cl_EntityLog.f_GetClassAttribute <Cl_ELogClassAttribute>(obj);

            if (classAtr == null)
            {
                return;
            }

            Cl_Log newEvent = null;

            if (this.f_IsChanged(obj))
            {
                StringBuilder sbAction = new StringBuilder();

                if (f_IsNew(obj))
                {
                    if (!string.IsNullOrWhiteSpace(textNew))
                    {
                        sbAction.AppendLine(textNew);
                    }
                    else
                    {
                        sbAction.AppendLine("Создан новый элемент");
                    }
                }
                else
                {
                    Dictionary <PropertyInfo, object> changedValues = f_GetChangedValues(obj);
                    foreach (KeyValuePair <PropertyInfo, object> item in changedValues)
                    {
                        Cl_ELogPropertyAttribute propAttr = item.Key.GetCustomAttributes(typeof(Cl_ELogPropertyAttribute), true).FirstOrDefault() as Cl_ELogPropertyAttribute;

                        string action = "";

                        if (!propAttr.p_IsComputedLog)
                        {
                            if (propAttr.p_IsCustomDescription)
                            {
                                if (!string.IsNullOrEmpty(propAttr.p_Description))
                                {
                                    action = propAttr.p_Description + ".";
                                }
                            }
                            else
                            {
                                action = "Изменилось поле: \"";

                                if (string.IsNullOrEmpty(propAttr.p_Description))
                                {
                                    action += item.Key.Name + "\".";
                                }
                                else
                                {
                                    action += propAttr.p_Description + "\".";
                                }
                            }
                        }

                        if (!propAttr.p_IgnoreValue)
                        {
                            if (!propAttr.p_IsNewValueOnly && propAttr.p_IsComputedLog == false)
                            {
                                action += " Старое значение: \"" + Cl_EntityValue.f_GetValue(item.Key, lastValues[item.Key], null) + "\".";
                            }

                            action += (propAttr.p_IsComputedLog ? "" : " Новое значение: \"") + Cl_EntityValue.f_GetValue(item.Key, item.Value, lastValues[item.Key]) + (propAttr.p_IsComputedLog ? "" : "\".");
                        }

                        sbAction.AppendLine(action);
                    }
                }

                newEvent = f_CreateEvent(obj, sbAction.ToString().Trim());
            }
            else
            {
                newEvent = f_CreateEvent(obj, "Без изменений");
            }


            Cl_App.m_DataContext.p_Logs.Add(newEvent);
            Cl_App.m_DataContext.SaveChanges();

            if (lastValues != null)
            {
                lastValues.Clear();
                lastValues = f_GetValues(obj);
            }

            logObject = obj;
        }