MetaModel.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / DynamicData / DynamicData / MetaModel.cs / 1305376 / MetaModel.cs

                            using System.Collections; 
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; 
using System.Globalization; 
using System.Linq;
using System.Web.DynamicData.ModelProviders; 
using System.Web.Resources;

#if !ORYX_VNEXT
using System.Collections.Concurrent; 
#endif
 
namespace System.Web.DynamicData { 
    /// 
    /// Object that represents a database or a number of databases used by the dynamic data. It can have multiple different data contexts registered on it. 
    /// 
    public class MetaModel : IMetaModel {
        private List _contextTypes = new List();
        private static object _lock = new object(); 
        private List _tables = new List();
        private ReadOnlyCollection _tablesRO; 
        private Dictionary _tablesByUniqueName = new Dictionary(StringComparer.OrdinalIgnoreCase); 
        private Dictionary _tablesByContextAndName = new Dictionary();
        private SchemaCreator _schemaCreator; 
        private EntityTemplateFactory _entityTemplateFactory;
        private IFieldTemplateFactory _fieldTemplateFactory;
        private FilterFactory _filterFactory;
        private static Exception s_registrationException; 
        private static MetaModel s_defaultModel;
        private string _dynamicDataFolderVirtualPath; 
        private HttpContextBase _context; 
#if !ORYX_VNEXT
        private readonly static ConcurrentDictionary s_registeredMetadataTypes = new ConcurrentDictionary(); 
#else
        private readonly static Dictionary s_registeredMetadataTypes = new Dictionary();
#endif
 
        // Use global registration is true by default
        private bool _registerGlobally = true; 
 
        internal virtual int RegisteredDataModelsCount {
            get { 
                return _contextTypes.Count;
            }
        }
 
        /// 
        /// ctor 
        ///  
        public MetaModel()
            : this(true /* registerGlobally */) { 
        }

        public MetaModel(bool registerGlobally)
            : this(SchemaCreator.Instance, registerGlobally) { 
        }
 
        // constructor for testing purposes 
        internal MetaModel(SchemaCreator schemaCreator, bool registerGlobally) {
            // Create a readonly wrapper for handing out 
            _tablesRO = new ReadOnlyCollection(_tables);
            _schemaCreator = schemaCreator;
            _registerGlobally = registerGlobally;
 
            // Don't touch Default.Model when we're not using global registration
            if (registerGlobally) { 
                lock (_lock) { 
                    if (Default == null) {
                        Default = this; 
                    }
                }
            }
        } 

        internal HttpContextBase Context { 
            get { 
                return _context ?? HttpContext.Current.ToWrapper();
            } 
            set {
                _context = value;
            }
        } 

        ///  
        /// allows for setting of the DynamicData folder for this mode. The default is ~/DynamicData/ 
        /// 
        public string DynamicDataFolderVirtualPath { 
            get {
                if (_dynamicDataFolderVirtualPath == null) {
                    _dynamicDataFolderVirtualPath = "~/DynamicData/";
                } 

                return _dynamicDataFolderVirtualPath; 
            } 
            set {
                // Make sure it ends with a slash 
                _dynamicDataFolderVirtualPath = VirtualPathUtility.AppendTrailingSlash(value);
            }
        }
 
        /// 
        /// Returns a reference to the first instance of MetaModel that is created in an app. Provides a simple way of referencing 
        /// the default MetaModel instance. Applications that will use multiple models will have to provide their own way of storing 
        /// references to any additional meta models. One way of looking them up is by using the GetModel method.
        ///  
        public static MetaModel Default {
            get {
                CheckForRegistrationException();
                return s_defaultModel; 
            }
            internal set { s_defaultModel = value; } 
        } 

        ///  
        /// Gets the model instance that had the contextType registered with it
        /// 
        /// A DataContext or ObjectContext type (e.g. NorthwindDataContext)
        /// a model 
        public static MetaModel GetModel(Type contextType) {
            CheckForRegistrationException(); 
            if (contextType == null) { 
                throw new ArgumentNullException("contextType");
            } 
            MetaModel model;
            if (MetaModelManager.TryGetModel(contextType, out model)) {
                return model;
            } 
            else {
                throw new InvalidOperationException(String.Format( 
                    CultureInfo.CurrentCulture, 
                    DynamicDataResources.MetaModel_ContextDoesNotBelongToModel,
                    contextType.FullName)); 
            }
        }

        ///  
        /// Registers a context. Uses the default ContextConfiguration options.
        ///  
        ///  
        public void RegisterContext(Type contextType) {
            RegisterContext(contextType, new ContextConfiguration()); 
        }

        /// 
        /// Registers a context. Uses the the given ContextConfiguration options. 
        /// 
        ///  
        ///  
        public void RegisterContext(Type contextType, ContextConfiguration configuration) {
            if (contextType == null) { 
                throw new ArgumentNullException("contextType");
            }
            RegisterContext(() => Activator.CreateInstance(contextType), configuration);
        } 

        ///  
        /// Registers a context. Uses default ContextConfiguration. Accepts a context factory that is a delegate used for 
        /// instantiating the context. This allows developers to instantiate context using a custom constructor.
        ///  
        /// 
        public void RegisterContext(Func contextFactory) {
            RegisterContext(contextFactory, new ContextConfiguration());
        } 

        ///  
        /// Registers a context. Uses given ContextConfiguration. Accepts a context factory that is a delegate used for 
        /// instantiating the context. This allows developers to instantiate context using a custom constructor.
        ///  
        /// 
        /// 
        public void RegisterContext(Func contextFactory, ContextConfiguration configuration) {
            object contextInstance = null; 
            try {
                if (contextFactory == null) { 
                    throw new ArgumentNullException("contextFactory"); 
                }
                contextInstance = contextFactory(); 
                if (contextInstance == null) {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaModel_ContextFactoryReturnsNull), "contextFactory");
                }
                Type contextType = contextInstance.GetType(); 
                if (!_schemaCreator.ValidDataContextType(contextType)) {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaModel_ContextTypeNotSupported, contextType.FullName)); 
                } 
            }
            catch (Exception e) { 
                s_registrationException = e;
                throw;
            }
 
            // create model abstraction
            RegisterContext(_schemaCreator.CreateDataModel(contextInstance, contextFactory), configuration); 
        } 

        ///  
        /// Register context using give model provider. Uses default context configuration.
        /// 
        /// 
        public void RegisterContext(DataModelProvider dataModelProvider) { 
            RegisterContext(dataModelProvider, new ContextConfiguration());
        } 
 
        /// 
        /// Register context using give model provider. Uses given context configuration. 
        /// 
        /// 
        /// 
        public void RegisterContext(DataModelProvider dataModelProvider, ContextConfiguration configuration) { 
            if (dataModelProvider == null) {
                throw new ArgumentNullException("dataModelProvider"); 
            } 

            if (_registerGlobally) { 
                CheckForRegistrationException();
            }

            if (configuration == null) { 
                throw new ArgumentNullException("configuration");
            } 
 
            // check if context has already been registered
            if (_contextTypes.Contains(dataModelProvider.ContextType)) { 
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaModel_ContextAlreadyRegistered, dataModelProvider.ContextType.FullName));
            }

            try { 
                IEnumerable tableProviders = dataModelProvider.Tables;
 
                // create and validate model 
                var tablesToInitialize = new List();
                foreach (TableProvider tableProvider in tableProviders) { 
                    RegisterMetadataTypeDescriptionProvider(tableProvider, configuration.MetadataProviderFactory);

                    MetaTable table = CreateTable(tableProvider);
                    table.CreateColumns(); 

                    var tableNameAttribute = tableProvider.Attributes.OfType().SingleOrDefault(); 
                    string nameOverride = tableNameAttribute != null ? tableNameAttribute.Name : null; 
                    table.SetScaffoldAndName(configuration.ScaffoldAllTables, nameOverride);
 
                    CheckTableNameConflict(table, nameOverride, tablesToInitialize);

                    tablesToInitialize.Add(table);
                } 

                _contextTypes.Add(dataModelProvider.ContextType); 
 
                if (_registerGlobally) {
                    MetaModelManager.AddModel(dataModelProvider.ContextType, this); 
                }

                foreach (MetaTable table in tablesToInitialize) {
                    AddTable(table); 
                }
                // perform initialization at the very end to ensure all references will be properly registered 
                foreach (MetaTable table in tablesToInitialize) { 
                    table.Initialize();
                } 
            }
            catch (Exception e) {
                if (_registerGlobally) {
                    s_registrationException = e; 
                }
                throw; 
            } 
        }
 
        internal static void CheckForRegistrationException() {
            if (s_registrationException != null) {
                throw new InvalidOperationException(
                    String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaModel_RegistrationErrorOccurred), 
                    s_registrationException);
            } 
        } 

        ///  
        /// Reset any previous registration error that may have happened. Normally, the behavior is that when an error
        /// occurs during registration, the exception is cached and rethrown on all subsequent operations. This is done
        /// so that if an error occurs in Application_Start, it shows up on every request.  Calling this method clears
        /// out the error and potentially allows new RegisterContext calls. 
        /// 
        public static void ResetRegistrationException() { 
            s_registrationException = null; 
        }
 
        // Used  for unit tests
        internal static void ClearSimpleCache() {
            s_registeredMetadataTypes.Clear();
        } 

        internal static MetaModel CreateSimpleModel(Type entityType) { 
            // Never register a TDP more than once for a type 
            if (!s_registeredMetadataTypes.ContainsKey(entityType)) {
                var provider = new AssociatedMetadataTypeTypeDescriptionProvider(entityType); 
#if !ORYX_VNEXT
                TypeDescriptor.AddProviderTransparent(provider, entityType);
                s_registeredMetadataTypes.TryAdd(entityType, true);
#else 
                MetadataHandlerRegistration.RegisterSingle(entityType, provider);
                s_registeredMetadataTypes.Add(entityType, true); 
#endif 
            }
 
            MetaModel model = new MetaModel(false /* registerGlobally */);

            // Pass a null provider factory since we registered the provider ourselves
            model.RegisterContext(new SimpleDataModelProvider(entityType), new ContextConfiguration { MetadataProviderFactory = null }); 
            return model;
        } 
 
        internal static MetaModel CreateSimpleModel(ICustomTypeDescriptor descriptor) {
            MetaModel model = new MetaModel(false /* registerGlobally */); 
            //
            model.RegisterContext(new SimpleDataModelProvider(descriptor));
            return model;
        } 

        ///  
        /// Instantiate a MetaTable object. Can be overridden to instantiate a derived type 
        /// 
        ///  
        protected virtual MetaTable CreateTable(TableProvider provider) {
            return new MetaTable(this, provider);
        }
 
        private void AddTable(MetaTable table) {
            _tables.Add(table); 
            _tablesByUniqueName.Add(table.Name, table); 
            if (_registerGlobally) {
                MetaModelManager.AddTable(table.EntityType, table); 
            }

            if (table.DataContextType != null) {
                // need to use the name from the provider since the name from the table could have been modified by use of TableNameAttribute 
                _tablesByContextAndName.Add(new ContextTypeTableNamePair(table.DataContextType, table.Provider.Name), table);
            } 
        } 

        private void CheckTableNameConflict(MetaTable table, string nameOverride, List tablesToInitialize) { 
            // try to find name conflict in tables from other context, or already processed tables in current context
            MetaTable nameConflictTable;
            if (!_tablesByUniqueName.TryGetValue(table.Name, out nameConflictTable)) {
                nameConflictTable = tablesToInitialize.Find(t => t.Name.Equals(table.Name, StringComparison.CurrentCulture)); 
            }
            if (nameConflictTable != null) { 
                if (String.IsNullOrEmpty(nameOverride)) { 
                    throw new ArgumentException(String.Format(
                        CultureInfo.CurrentCulture, 
                        DynamicDataResources.MetaModel_EntityNameConflict,
                        table.EntityType.FullName,
                        table.DataContextType.FullName,
                        nameConflictTable.EntityType.FullName, 
                        nameConflictTable.DataContextType.FullName));
                } 
                else { 
                    throw new ArgumentException(String.Format(
                        CultureInfo.CurrentCulture, 
                        DynamicDataResources.MetaModel_EntityNameOverrideConflict,
                        nameOverride,
                        table.EntityType.FullName,
                        table.DataContextType.FullName, 
                        nameConflictTable.EntityType.FullName,
                        nameConflictTable.DataContextType.FullName)); 
                } 
            }
        } 

        private static void RegisterMetadataTypeDescriptionProvider(TableProvider entity, Func providerFactory) {
            if (providerFactory != null) {
                Type entityType = entity.EntityType; 
                // Support for type-less MetaTable
                if (entityType != null) { 
                    TypeDescriptionProvider provider = providerFactory(entityType); 
                    if (provider != null) {
#if ORYX_VNEXT 
                        MetadataHandlerRegistration.RegisterSingle(entityType, provider);
#else
                        TypeDescriptor.AddProviderTransparent(provider, entityType);
#endif 
                    }
                } 
            } 
        }
 
        /// 
        /// Returns a collection of all the tables that are part of the context, regardless of whether they are visible or not.
        /// 
        public ReadOnlyCollection Tables { 
            get {
                CheckForRegistrationException(); 
                return _tablesRO; 
            }
        } 

        /// 
        /// Returns a collection of the currently visible tables for this context. Currently visible is defined as:
        /// - a table whose EntityType is not abstract 
        /// - a table with scaffolding enabled
        /// - a table for which a custom page for the list action can be found and that can be read by the current User 
        ///  
        public List VisibleTables {
            get { 
                CheckForRegistrationException();
                return Tables.Where(IsTableVisible).OrderBy(t => t.DisplayName).ToList();
            }
        } 

        private bool IsTableVisible(MetaTable table) { 
            return !table.EntityType.IsAbstract && !String.IsNullOrEmpty(table.ListActionPath) && table.CanRead(Context.User); 
        }
 
        /// 
        /// Looks up a MetaTable by the entity type. Throws an exception if one is not found.
        /// 
        ///  
        /// 
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We really want this to be a Type.")] 
        public MetaTable GetTable(Type entityType) { 
            MetaTable table;
            if (!TryGetTable(entityType, out table)) { 
                throw new ArgumentException(String.Format(
                    CultureInfo.CurrentCulture,
                    DynamicDataResources.MetaModel_UnknownEntityType,
                    entityType.FullName)); 
            }
 
            return table; 
        }
 
        /// 
        /// Tries to look up a MetaTable by the entity type.
        /// 
        ///  
        /// 
        ///  
        public bool TryGetTable(Type entityType, out MetaTable table) { 
            CheckForRegistrationException();
 
            if (entityType == null) {
                throw new ArgumentNullException("entityType");
            }
 
            if (!_registerGlobally) {
                table = Tables.SingleOrDefault(t => t.EntityType == entityType); 
                return table != null; 
            }
 
            return MetaModelManager.TryGetTable(entityType, out table);
        }

        ///  
        /// Looks up a MetaTable by unique name. Throws if one is not found. The unique name defaults to the table name, or an override
        /// can be provided via ContextConfiguration when the context that contains the table is registered. The unique name uniquely 
        /// identifies a table within a give MetaModel. It is used for URL generation. 
        /// 
        ///  
        /// 
        public MetaTable GetTable(string uniqueTableName) {
            CheckForRegistrationException();
 
            MetaTable table;
            if (!TryGetTable(uniqueTableName, out table)) { 
                throw new ArgumentException(String.Format( 
                    CultureInfo.CurrentCulture,
                    DynamicDataResources.MetaModel_UnknownTable, 
                    uniqueTableName));
            }

            return table; 
        }
 
        ///  
        /// Tries to look up a MetaTable by unique name. Doe
        ///  
        /// 
        /// 
        /// 
        public bool TryGetTable(string uniqueTableName, out MetaTable table) { 
            CheckForRegistrationException();
 
            if (uniqueTableName == null) { 
                throw new ArgumentNullException("uniqueTableName");
            } 

            return _tablesByUniqueName.TryGetValue(uniqueTableName, out table);
        }
 
        /// 
        /// Looks up a MetaTable by the contextType/tableName combination. Throws if one is not found. 
        ///  
        /// 
        ///  
        /// 
        public MetaTable GetTable(string tableName, Type contextType) {
            CheckForRegistrationException();
 
            if (tableName == null) {
                throw new ArgumentNullException("tableName"); 
            } 

            if (contextType == null) { 
                throw new ArgumentNullException("contextType");
            }

            MetaTable table; 
            if (!_tablesByContextAndName.TryGetValue(new ContextTypeTableNamePair(contextType, tableName), out table)) {
                if (!_contextTypes.Contains(contextType)) { 
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, 
                        DynamicDataResources.MetaModel_UnknownContextType,
                        contextType.FullName)); 
                }
                else {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
                        DynamicDataResources.MetaModel_UnknownTableInContext, 
                        contextType.FullName,
                        tableName)); 
                } 
            }
            return table; 
        }

        /// 
        /// Lets you set a custom IFieldTemplateFactory. An IFieldTemplateFactor lets you customize which field templates are created 
        /// for the various columns.
        ///  
        public IFieldTemplateFactory FieldTemplateFactory { 
            get {
                // If no custom factory was set, use our default 
                if (_fieldTemplateFactory == null) {
                    FieldTemplateFactory = new FieldTemplateFactory();
                }
 
                return _fieldTemplateFactory;
            } 
            set { 
                _fieldTemplateFactory = value;
 
                // Give the model to the factory
                if (_fieldTemplateFactory != null) {
                    _fieldTemplateFactory.Initialize(this);
                } 
            }
        } 
 
        public EntityTemplateFactory EntityTemplateFactory {
            get { 
                if (_entityTemplateFactory == null) {
                    EntityTemplateFactory = new EntityTemplateFactory();
                }
 
                return _entityTemplateFactory;
            } 
            set { 
                _entityTemplateFactory = value;
                if (_entityTemplateFactory != null) { 
                    _entityTemplateFactory.Initialize(this);
                }
            }
        } 

        public FilterFactory FilterFactory { 
            get { 
                if (_filterFactory == null) {
                    FilterFactory = new FilterFactory(); 
                }
                return _filterFactory;
            }
            set { 
                _filterFactory = value;
                if (_filterFactory != null) { 
                    _filterFactory.Initialize(this); 
                }
            } 
        }

        private string _queryStringKeyPrefix = String.Empty;
 
        /// 
        /// Lets you get an action path (URL) to an action for a particular table/action/entity instance combo. 
        ///  
        /// 
        ///  
        /// An object representing a single row of data in a table. Used to provide values for query string parameters.
        /// 
        public string GetActionPath(string tableName, string action, object row) {
            return GetTable(tableName).GetActionPath(action, row); 
        }
 
        private class ContextTypeTableNamePair : IEquatable { 
            public ContextTypeTableNamePair(Type contextType, string tableName) {
                Debug.Assert(contextType != null); 
                Debug.Assert(tableName != null);

                ContextType = contextType;
                TableName = tableName; 

                HashCode = ContextType.GetHashCode() ^ TableName.GetHashCode(); 
            } 

            private int HashCode { get; set; } 
            public Type ContextType { get; private set; }
            public string TableName { get; private set; }

            public bool Equals(ContextTypeTableNamePair other) { 
                if (other == null) {
                    return false; 
                } 
                return ContextType == other.ContextType && TableName.Equals(other.TableName, StringComparison.Ordinal);
            } 

            public override int GetHashCode() {
                return HashCode;
            } 

            public override bool Equals(object obj) { 
                return Equals(obj as ContextTypeTableNamePair); 
            }
        } 

        internal static class MetaModelManager {
            private static Hashtable s_modelByContextType = new Hashtable();
            private static Hashtable s_tableByEntityType = new Hashtable(); 

            internal static void AddModel(Type contextType, MetaModel model) { 
                Debug.Assert(contextType != null); 
                Debug.Assert(model != null);
                lock (s_modelByContextType) { 
                    s_modelByContextType.Add(contextType, model);
                }
            }
 
            internal static bool TryGetModel(Type contextType, out MetaModel model) {
                model = (MetaModel)s_modelByContextType[contextType]; 
                return model != null; 
            }
 
            internal static void AddTable(Type entityType, MetaTable table) {
                Debug.Assert(entityType != null);
                Debug.Assert(table != null);
                lock (s_tableByEntityType) { 
                    s_tableByEntityType[entityType] = table;
                } 
            } 

            internal static void Clear() { 
                lock (s_modelByContextType) {
                    s_modelByContextType.Clear();
                }
                lock (s_tableByEntityType) { 
                    s_tableByEntityType.Clear();
                } 
            } 

            internal static bool TryGetTable(Type type, out MetaTable table) { 
                table = (MetaTable)s_tableByEntityType[type];
                return table != null;
            }
        } 

        ReadOnlyCollection IMetaModel.Tables { 
            get { 
                return Tables.OfType().ToList().AsReadOnly();
            } 
        }

        bool IMetaModel.TryGetTable(string uniqueTableName, out IMetaTable table) {
            MetaTable metaTable; 
            table = null;
            if (TryGetTable(uniqueTableName, out metaTable)) { 
                table = metaTable; 
                return true;
            } 
            return false;
        }

        bool IMetaModel.TryGetTable(Type entityType, out IMetaTable table) { 
            MetaTable metaTable;
            table = null; 
            if (TryGetTable(entityType, out metaTable)) { 
                table = metaTable;
                return true; 
            }
            return false;
        }
 
        List IMetaModel.VisibleTables {
            get { 
                return VisibleTables.OfType().ToList(); 
            }
        } 

        IMetaTable IMetaModel.GetTable(string tableName, Type contextType) {
            return GetTable(tableName, contextType);
        } 

        IMetaTable IMetaModel.GetTable(string uniqueTableName) { 
            return GetTable(uniqueTableName); 
        }
 
        IMetaTable IMetaModel.GetTable(Type entityType) {
            return GetTable(entityType);
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK