using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Principal;
using System.Web.DynamicData.ModelProviders;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.Routing;
using System.Web.UI;
using System.Web.UI.WebControls;
using AttributeCollection = System.ComponentModel.AttributeCollection;
namespace System.Web.DynamicData {
///
/// Represents a database table for use by dynamic data pages
///
public class MetaTable : IMetaTable {
private const int DefaultColumnOrder = 10000;
private Dictionary _columnsByName;
private HttpContextBase _context;
private MetaColumn _displayColumn;
private string _foreignKeyColumnsNames;
private bool? _hasToStringOverride;
private MetaTableMetadata _metadata;
private ReadOnlyCollection _primaryKeyColumns;
private string[] _primaryKeyColumnNames;
private bool _scaffoldDefaultValue;
private MetaColumn _sortColumn;
private bool _sortColumnProcessed;
private TableProvider _tableProvider;
private string _listActionPath;
///
/// A collection of attributes declared on this entity type (i.e. class-level attributes).
///
public AttributeCollection Attributes {
get {
return Metadata.Attributes;
}
}
///
/// All columns
///
public ReadOnlyCollection Columns {
get;
// internal for unit testing
internal set;
}
// for unit testing
internal HttpContextBase Context {
private get {
return _context ?? new HttpContextWrapper(HttpContext.Current);
}
set {
_context = value;
}
}
///
/// Name of table coming from the property on the data context. E.g. the value is "Products" for a table that is part of
/// the NorthwindDataContext.Products collection.
///
public string DataContextPropertyName {
get {
return _tableProvider.DataContextPropertyName;
}
}
///
/// The type of the data context this table belongs to.
///
public Type DataContextType {
get {
return Provider.DataModel.ContextType;
}
}
///
/// Returns the column being used for display values when entries in this table are used as parents in foreign key relationships.
/// Which column to use can be specified using DisplayColumnAttribute. If the attribute is not present, the following heuristic is used:
/// 1. First non-PK string column
/// 2. First PK string column
/// 3. First PK non-string column
/// 4. First column
///
public virtual MetaColumn DisplayColumn {
get {
// use a local to avoid a null value if ResetMetadata gets called
var displayColumn = _displayColumn;
if (displayColumn == null) {
displayColumn = GetDisplayColumnFromMetadata() ?? GetDisplayColumnFromHeuristic();
_displayColumn = displayColumn;
}
return displayColumn;
}
}
///
/// Gets the string to be user-friendly string representing this table. Defaults to the value of the Name property.
/// Can be customized using DisplayNameAttribute.
///
[SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces",
Justification = "Interface denotes existence of property, not used for security.")]
public virtual string DisplayName {
get {
return Metadata.DisplayName ?? Name;
}
}
///
/// Return the type of the Entity represented by this table (e.g. Product)
///
public Type EntityType {
get {
return Provider.EntityType;
}
}
///
/// Get a comma separated list of foreign key names. This is useful to set the IncludePaths on an EntityDataSource
///
public string ForeignKeyColumnsNames {
get {
if (_foreignKeyColumnsNames == null) {
var fkColumnNamesArray = Columns.OfType().Select(column => column.Name).ToArray();
_foreignKeyColumnsNames = String.Join(",", fkColumnNamesArray);
}
return _foreignKeyColumnsNames;
}
}
///
/// Returns true if the table has a primary key
///
public bool HasPrimaryKey {
get {
// Some of the columns may be primary keys, but if this is a view, it doesn't "have"
// any primary keys, so PrimaryKey is null.
return PrimaryKeyColumns.Count > 0;
}
}
private bool HasToStringOverride {
get {
// Check if the entity type overrides ToString()
//
if (!_hasToStringOverride.HasValue) {
MethodInfo toStringMethod = EntityType.GetMethod("ToString");
_hasToStringOverride = (toStringMethod.DeclaringType != typeof(object));
}
return _hasToStringOverride.Value;
}
}
///
/// Returns true if this is a read-only table or view(has not PK).
///
[SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces",
Justification = "Interface denotes existence of property, not used for security.")]
public virtual bool IsReadOnly {
get {
return Metadata.IsReadOnly || !HasPrimaryKey;
}
}
///
/// Gets the action path to the list action for this table
///
public string ListActionPath {
get {
return _listActionPath ?? GetActionPath(PageAction.List);
}
internal set {
_listActionPath = value;
}
}
private MetaTableMetadata Metadata {
get {
// use a local to avoid returning null if ResetMetadata gets called
var metadata = _metadata;
if (metadata == null) {
metadata = new MetaTableMetadata(this);
_metadata = metadata;
}
return metadata;
}
}
///
/// The model this table belongs to.
///
public MetaModel Model { get; private set; }
///
/// Unique name of table. This name is unique within a given data context. (e.g. "MyCustomName_Products")
///
public string Name {
get;
private set;
}
///
/// Columns that constitute the primary key of this table
///
public ReadOnlyCollection PrimaryKeyColumns {
get {
if (_primaryKeyColumns == null) {
_primaryKeyColumns = Columns.Where(c => c.IsPrimaryKey).ToList().AsReadOnly();
}
return _primaryKeyColumns;
}
}
internal string[] PrimaryKeyNames {
get {
if (_primaryKeyColumnNames == null) {
_primaryKeyColumnNames = PrimaryKeyColumns.Select(c => c.Name).ToArray();
}
return _primaryKeyColumnNames;
}
}
///
/// The underlying provider for this column
///
public TableProvider Provider { get { return _tableProvider; } }
///
/// Return the root type of this entity's inheritance hierarchy; if the type is at the top
/// of an inheritance hierarchy or does not have any inheritance, will return EntityType.
///
public Type RootEntityType {
get {
return Provider.RootEntityType;
}
}
///
/// Whether or not to scaffold. This can be customized using ScaffoldAttribute
///
[SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces",
Justification = "Interface denotes existence of property, not used for security.")]
public virtual bool Scaffold {
get {
return Metadata.ScaffoldTable ?? _scaffoldDefaultValue;
}
}
///
/// Gets the column used as the sorting column when used FK relationships. Defaults to the same column that is returned by DisplayColumn.
/// Can be customized using options on DisplayColumnAttribute.
///
public virtual MetaColumn SortColumn {
get {
if (!_sortColumnProcessed) {
var displayColumnAttribute = Metadata.DisplayColumnAttribute;
if (displayColumnAttribute != null && !String.IsNullOrEmpty(displayColumnAttribute.SortColumn)) {
if (!TryGetColumn(displayColumnAttribute.SortColumn, out _sortColumn)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.MetaTable_CantFindSortColumn,
displayColumnAttribute.SortColumn,
Name));
}
if (_sortColumn is MetaChildrenColumn) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.MetaTable_CantUseChildrenColumnAsSortColumn,
_sortColumn.Name,
Name));
}
}
_sortColumnProcessed = true;
}
return _sortColumn;
}
}
///
/// Returns true if the entries in this column are meant to be sorted in a descending order when used as parents in a FK relationship.
/// Can be declared using options on DisplayColumnAttribute
///
[SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces",
Justification = "Interface denotes existence of property, not used for security.")]
public virtual bool SortDescending {
get {
return Metadata.SortDescending;
}
}
public MetaTable(MetaModel metaModel, TableProvider tableProvider) {
_tableProvider = tableProvider;
Model = metaModel;
}
///
/// Build the attribute collection, made publicly available through the Attributes property
///
protected virtual AttributeCollection BuildAttributeCollection() {
return Provider.Attributes;
}
///
/// Returns whether the passed in user is allowed to delete items from the table
///
public virtual bool CanDelete(IPrincipal principal) {
return Provider.CanDelete(principal);
}
///
/// Returns whether the passed in user is allowed to insert into the table
///
public virtual bool CanInsert(IPrincipal principal) {
return Provider.CanInsert(principal);
}
///
/// Returns whether the passed in user is allowed to read from the table
///
public virtual bool CanRead(IPrincipal principal) {
return Provider.CanRead(principal);
}
///
/// Returns whether the passed in user is allowed to make changes tothe table
///
public virtual bool CanUpdate(IPrincipal principal) {
return Provider.CanUpdate(principal);
}
public static MetaTable CreateTable(Type entityType) {
return MetaModel.CreateSimpleModel(entityType).Tables.First();
}
public static MetaTable CreateTable(ICustomTypeDescriptor typeDescriptor) {
return MetaModel.CreateSimpleModel(typeDescriptor).Tables.First();
}
///
/// Instantiate a MetaChildrenColumn object. Can be overridden to instantiate a derived type
///
///
protected virtual MetaChildrenColumn CreateChildrenColumn(ColumnProvider columnProvider) {
return new MetaChildrenColumn(this, columnProvider);
}
///
/// Instantiate a MetaColumn object. Can be overridden to instantiate a derived type
///
///
protected virtual MetaColumn CreateColumn(ColumnProvider columnProvider) {
return new MetaColumn(this, columnProvider);
}
private MetaColumn CreateColumnInternal(ColumnProvider columnProvider) {
if (columnProvider.Association != null) {
switch (columnProvider.Association.Direction) {
case AssociationDirection.OneToOne:
case AssociationDirection.ManyToOne:
return CreateForeignKeyColumn(columnProvider);
case AssociationDirection.ManyToMany:
case AssociationDirection.OneToMany:
return CreateChildrenColumn(columnProvider);
}
Debug.Assert(false);
}
return CreateColumn(columnProvider);
}
internal void CreateColumns() {
var columns = new List();
_columnsByName = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (ColumnProvider columnProvider in Provider.Columns) {
MetaColumn column = CreateColumnInternal(columnProvider);
columns.Add(column);
if (_columnsByName.ContainsKey(column.Name)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaTable_ColumnNameConflict,
column.Name, Provider.Name));
}
_columnsByName.Add(column.Name, column);
}
Columns = new ReadOnlyCollection(columns);
}
///
/// Instantiate a data context that this table belongs to. Uses the instatiotion method specified when the context was registered.
///
///
public virtual object CreateContext() {
return Provider.DataModel.CreateContext();
}
///
/// Instantiate a MetaForeignKeyColumn object. Can be overridden to instantiate a derived type
///
///
protected virtual MetaForeignKeyColumn CreateForeignKeyColumn(ColumnProvider columnProvider) {
return new MetaForeignKeyColumn(this, columnProvider);
}
///
/// Gets the action path for the given row (to get primary key values for query string filters, etc.)
///
///
/// the instance of the row
///
public string GetActionPath(string action, object row) {
// Delegate to the overload that takes an array of primary key values
return GetActionPath(action, GetPrimaryKeyValues(row));
}
///
/// Gets the action path for the given row (to get primary key values for query string filters, etc.)
///
///
/// the instance of the row
///
///
public string GetActionPath(string action, object row, string path) {
// Delegate to the overload that takes an array of primary key values
return GetActionPath(action, GetPrimaryKeyValues(row), path);
}
///
/// Gets the action path for the current table and the passed in action
///
public string GetActionPath(string action) {
return GetActionPath(action, (IList