//******************************************************************************************************
// DataModelBase.cs - Gbtc
//
// Copyright © 2012, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 03/25/2011 - Mehulbhai P Thakkar
// Generated original version of source code.
// 04/08/2011 - J. Ritchie Carroll
// Modified class to use reflection to load default values and use entity property attributes.
// 12/20/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using GSF.Collections;
using GSF.Data;
using GSF.Reflection;
namespace GSF.TimeSeries.UI
{
///
/// Represents an abstract base class for all entity model objects.
///
public abstract class DataModelBase : IDataModel
{
#region [ Members ]
// Constants
///
/// The default timeout duration used for executing SQL statements.
///
public const int DefaultTimeout = DataExtensions.DefaultTimeoutDuration;
// Events
///
/// Raised when a property on this object has a new value.
///
public event PropertyChangedEventHandler PropertyChanged;
// Fields
private readonly ConcurrentDictionary m_propertyErrors;
private BindingFlags m_memberAccessBindingFlags;
private bool m_requireEntityPropertyAttribute;
private bool m_lastIsValidState;
private static bool m_messageFlag;
#endregion
#region [ Constructors ]
///
/// Creates a new instance of the class.
///
protected DataModelBase()
: this(false)
{
}
///
/// Creates a new instance of the class.
///
///
/// Assigns flag that determines if is required
/// to exist before a property is included as a field in the data model.
///
protected DataModelBase(bool requireEntityPropertyAttribute)
: this(true, requireEntityPropertyAttribute)
{
}
///
/// Creates a new instance of the class.
///
///
/// Determines whether to load default values into the properties using reflection.
///
///
/// Assigns flag that determines if is required
/// to exist before a property is included as a field in the data model.
///
protected DataModelBase(bool loadDefaults, bool requireEntityPropertyAttribute)
{
Action propertyAction = property =>
{
object defaultValue = DeriveDefaultValue(property.Name, property.GetValue(this, null));
if (!Common.IsDefaultValue(defaultValue))
property.SetValue(this, defaultValue, null);
OnPropertyChanged(property.Name);
};
m_propertyErrors = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
m_memberAccessBindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
m_requireEntityPropertyAttribute = requireEntityPropertyAttribute;
if (loadDefaults)
{
// Load all default values for properties
ExecuteActionForProperties(propertyAction, property => (object)property.GetGetMethod() != null && (object)property.GetSetMethod() != null);
}
}
#endregion
#region [ Properties ]
///
/// Indicates if the values associated with this object are valid.
///
[EntityProperty(false)]
public bool IsValid
{
get
{
// If any of the properties have errors, values are not valid
m_lastIsValidState = m_propertyErrors.All(kvPair => string.IsNullOrWhiteSpace(kvPair.Value));
return m_lastIsValidState;
}
}
///
/// Gets an error message indicating what is wrong with this object.
///
///
/// An error message indicating what is wrong with this object. The default is an empty string ("").
///
[EntityProperty(false)]
public string Error
{
get
{
// Cumulate any properties with errors using line feed as delimiter
return m_propertyErrors.Where(kvPair => !string.IsNullOrWhiteSpace(kvPair.Value)).ToDelimitedString(Environment.NewLine);
}
}
///
/// Gets the error message for the property with the given name.
///
/// The name of the property whose error message to get.
/// The error message for the property. The default is an empty string ("").
public string this[string propertyName]
{
get
{
string error;
if (!m_propertyErrors.TryGetValue(propertyName, out error))
error = "";
return error;
}
}
///
/// Gets or sets flag that determines if is
/// required to exist before a property is included as a field in the data model;
/// defaults to false.
///
[Browsable(false), EntityProperty(false)]
public bool RequireEntityPropertyAttribute
{
get
{
return m_requireEntityPropertyAttribute;
}
set
{
m_requireEntityPropertyAttribute = value;
}
}
///
/// Gets or sets used to access properties of derived class.
///
///
/// Value defaults to | | .
///
[EditorBrowsable(EditorBrowsableState.Advanced), EntityProperty(false)]
protected virtual BindingFlags MemberAccessBindingFlags
{
get
{
return m_memberAccessBindingFlags;
}
set
{
m_memberAccessBindingFlags = value;
}
}
///
/// gets or sets when the current 's Message Flag
///
public static bool MessageFlag
{
get
{
return m_messageFlag;
}
set
{
m_messageFlag = value;
}
}
#endregion
#region [ Methods ]
///
/// Raises the event.
///
/// Property name that has changed.
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (string.Compare(propertyName, "IsValid", true) != 0)
ValidateProperty(propertyName);
}
///
/// Validates current instance properties using Data Annotations.
///
/// This instance property to validate.
/// Relevant error string on validation failure or on validation success.
protected virtual void ValidateProperty(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("Invalid property name", propertyName);
string error = "";
object value = GetType().GetProperty(propertyName).GetValue(this, null);
List results = new List(1);
ValidationContext validationContext = new ValidationContext(this, null, null)
{
MemberName = propertyName
};
if (!Validator.TryValidateProperty(value, validationContext, results))
{
ValidationResult validationResult = results.First();
error = validationResult.ErrorMessage;
}
m_propertyErrors[propertyName] = error;
if (m_lastIsValidState != IsValid)
OnPropertyChanged("IsValid");
}
///
/// Gets the default value specified by , if any, applied to the specified property.
///
/// Property name.
/// Default value applied to specified property; or null if one does not exist.
/// cannot be null or empty.
public object GetDefaultValue(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("name cannot be null or empty");
return GetAttributeValue(propertyName, null, attribute => attribute.Value);
}
///
/// Attempts to get best default value for given member.
///
/// Property name.
/// Current property value.
///
/// If is equal to its default(type) value, then any value derived from will be used instead.
///
/// The object that is the best default value.
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected object DeriveDefaultValue(string propertyName, object value)
{
// See if value is equal to its default value (i.e., uninitialized)
if (Common.IsDefaultValue(value))
{
// See if any value exists in a DefaultValueAttribute
object defaultValue = GetDefaultValue(propertyName);
if (defaultValue != null)
return defaultValue;
}
return value;
}
///
/// Executes specified action over all public derived class properties.
///
/// Action to execute for all properties.
/// Filter used to select which properties to execute the action.
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected void ExecuteActionForProperties(Action propertyAction, Func propertyFilter)
{
PropertyInfo[] members = GetType()
.GetProperties(m_memberAccessBindingFlags)
.Where(propertyFilter)
.ToArray();
ExecuteActionForMembers(property =>
{
// Make sure only non-indexer properties are used for settings
if (property.GetIndexParameters().Length == 0)
propertyAction(property);
}, members);
}
// Execute specified action over specified members
private void ExecuteActionForMembers(Action memberAction, T[] members) where T : MemberInfo
{
EntityPropertyAttribute attribute;
// Execute action for each member
foreach (T member in members)
{
// See if serialize setting attribute exists
if (member.TryGetAttribute(out attribute))
{
// Found entity property attribute, perform action if include is true
if (attribute.Include)
memberAction(member);
}
else if (!m_requireEntityPropertyAttribute)
{
// Didn't find entity property attribute and it's not required, so we perform action (i.e., assume include)
memberAction(member);
}
}
}
///
/// Attempts to find specified attribute and return specified value.
///
/// Type of to find.
/// Type of value attribute delegate returns.
/// Name of property to search for attribute.
/// Default value to return if attribute doesn't exist.
/// Function delegate used to return desired attribute property.
/// Specified attribute value if it exists; otherwise default value.
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected TValue GetAttributeValue(string propertyName, TValue defaultValue, Func attributeValue) where TAttribute : Attribute
{
TAttribute attribute;
// See if property exists with specified name
PropertyInfo property = this.GetType().GetProperty(propertyName, m_memberAccessBindingFlags);
if (property != null)
{
// See if attribute exists on property
if (property.TryGetAttribute(out attribute))
{
// Return value as specified by delegate
return attributeValue(attribute);
}
// Attribute wasn't found, return default value
return defaultValue;
}
// Return default value
return defaultValue;
}
#endregion
#region [ Static ]
// Static Methods
///
/// Creates and instance of if it is null.
///
/// Reference parameter representing object.
/// Boolean, true if new instance of is created otherwise false.
public static bool CreateConnection(ref AdoDataConnection database)
{
if (database == null)
{
database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory);
return true;
//try
//{
// database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory);
// return true;
//}
//catch (Exception ex)
//{
// throw;
// if (!MessageFlag)
// {
// m_messageFlag = true;
// MessageBox.Show("ERROR: " + ex.Message, "Create Database Connection", MessageBoxButton.OK);
// m_messageFlag = false;
// }
// return false;
//}
}
return false;
}
#endregion
}
}