Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -0,0 +1,67 @@
namespace System.Web.DynamicData.Util {
using System;
using System.Collections.Generic;
using System.Linq;
internal static class AttributeExtensions {
/// <summary>
/// Gets the first attribute of a given time on the target AttributeCollection, or null.
/// </summary>
/// <typeparam name="TAttribute">The attribute type</typeparam>
/// <param name="attributes">The AttributeCollection object</param>
/// <returns></returns>
internal static TAttribute FirstOrDefault<TAttribute>(this System.ComponentModel.AttributeCollection attributes) where TAttribute : Attribute {
return attributes.OfType<TAttribute>().FirstOrDefault();
}
internal static TResult GetAttributePropertyValue<TAttribute, TResult>(this System.ComponentModel.AttributeCollection attributes, Func<TAttribute, TResult> propertyGetter)
where TResult : class
where TAttribute : Attribute {
return attributes.GetAttributePropertyValue(propertyGetter, null);
}
internal static TResult GetAttributePropertyValue<TAttribute, TResult>(this System.ComponentModel.AttributeCollection attributes, Func<TAttribute, TResult> propertyGetter, TResult defaultValue)
where TAttribute : Attribute {
var attribute = attributes.FirstOrDefault<TAttribute>();
return attribute.GetPropertyValue<TAttribute, TResult>(propertyGetter, defaultValue);
}
/// <summary>
/// Gets the property for a given attribute reference or returns null if the reference is null.
/// </summary>
/// <typeparam name="TAttribute">The attribute type</typeparam>
/// <typeparam name="TResult">The type of the attribute's property</typeparam>
/// <param name="attribute">The attribute reference</param>
/// <param name="propertyGetter">The function to evaluate on the attribute</param>
/// <returns></returns>
internal static TResult GetPropertyValue<TAttribute, TResult>(this TAttribute attribute, Func<TAttribute, TResult> propertyGetter)
where TResult : class
where TAttribute : Attribute {
return attribute.GetPropertyValue(propertyGetter, null);
}
/// <summary>
/// Gets the property for a given attribute reference or returns the default value if the reference is null.
/// </summary>
/// <typeparam name="TAttribute">The attribute type</typeparam>
/// <typeparam name="TResult">The type of the attribute's property</typeparam>
/// <param name="attribute">The attribute reference</param>
/// <param name="propertyGetter">The function to evaluate on the attribute</param>
/// <param name="defaultValue">The default value to return if the attribute is null</param>
/// <returns></returns>
internal static TResult GetPropertyValue<TAttribute, TResult>(this TAttribute attribute, Func<TAttribute, TResult> propertyGetter, TResult defaultValue)
where TAttribute : Attribute {
if (attribute != null) {
return propertyGetter(attribute);
}
else {
return defaultValue;
}
}
}
}

View File

@@ -0,0 +1,54 @@
namespace System.Web.DynamicData.Util {
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Resources;
using System.Globalization;
using IDataBoundControlInterface = System.Web.UI.WebControls.IDataBoundControl;
internal static class DataControlHelper {
internal static IDynamicDataSource FindDataSourceControl(Control current) {
for (; ; current = current.NamingContainer) {
// Don't look further than the Page, or if the control is not added to a page hierarchy
if (current == null || current is Page)
return null;
IDataBoundControlInterface dataBoundControl = GetDataBoundControl(current, false /*failIfNotFound*/);
// Not a data control: continue searching
if (dataBoundControl == null) {
continue;
}
// Return its DynamicDataSource
return dataBoundControl.DataSourceObject as IDynamicDataSource;
}
}
internal static IDataBoundControlInterface GetDataBoundControl(Control control, bool failIfNotFound) {
if (control is IDataBoundControlInterface) {
return (IDataBoundControlInterface)control;
}
IDataBoundControlInterface dataBoundControl = null;
if (control is Repeater) {
dataBoundControl = GetControlAdapter(control);
}
if (dataBoundControl == null && failIfNotFound) {
throw new Exception(String.Format(
CultureInfo.CurrentCulture,
DynamicDataResources.DynamicDataManager_UnsupportedControl,
control.GetType()));
}
return dataBoundControl;
}
internal static IDataBoundControlInterface GetControlAdapter(Control control) {
Repeater repeater = control as Repeater;
if (repeater != null) {
return new RepeaterDataBoundAdapter(repeater);
}
return null;
}
}
}

View File

@@ -0,0 +1,32 @@
namespace System.Web.DynamicData.Util {
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
internal class DefaultValueMapping {
private IDictionary<string, object> _defaultValues;
public DictionaryCustomTypeDescriptor Instance {
get;
private set;
}
public IDictionary<string, object> Values {
get {
return _defaultValues;
}
}
public DefaultValueMapping(IDictionary<string, object> defaultValues) {
Debug.Assert(defaultValues != null);
_defaultValues = defaultValues;
// Build a custom type descriptor which will act as a lightweight wrapper around the dictionary.
Instance = new DictionaryCustomTypeDescriptor(defaultValues);
}
public bool Contains(MetaColumn column) {
Debug.Assert(_defaultValues != null);
return Misc.IsColumnInDictionary(column, _defaultValues);
}
}
}

View File

@@ -0,0 +1,85 @@
namespace System.Web.DynamicData.Util {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
internal class DictionaryCustomTypeDescriptor : CustomTypeDescriptor {
private IDictionary<string, object> _values;
private PropertyDescriptorCollection _properties;
public DictionaryCustomTypeDescriptor(IDictionary<string, object> values) {
if (values == null) {
throw new ArgumentNullException("values");
}
_values = values;
}
public object GetValue(string name) {
object value;
if (_values.TryGetValue(name, out value)) {
return value;
}
return null;
}
public override PropertyDescriptorCollection GetProperties() {
if (_properties == null) {
var dictionaryProps = _values.Keys.Select(propName => new DictionaryPropertyDescriptor(propName));
_properties = new PropertyDescriptorCollection(dictionaryProps.ToArray());
}
return _properties;
}
}
internal class DictionaryPropertyDescriptor : PropertyDescriptor {
public DictionaryPropertyDescriptor(string name)
: base(name, null /* attrs */) {
}
public override bool CanResetValue(object component) {
throw new NotSupportedException();
}
public override Type ComponentType {
get {
throw new NotSupportedException();
}
}
public override object GetValue(object component) {
// Try to cast the component to a DictionaryCustomTypeDescriptor and get the value in the dictonary
// that corresponds to this property
DictionaryCustomTypeDescriptor typeDescriptor = component as DictionaryCustomTypeDescriptor;
if (typeDescriptor == null) {
return null;
}
return typeDescriptor.GetValue(Name);
}
public override bool IsReadOnly {
get {
throw new NotSupportedException();
}
}
public override Type PropertyType {
get {
throw new NotSupportedException();
}
}
public override void ResetValue(object component) {
throw new NotSupportedException();
}
public override void SetValue(object component, object value) {
throw new NotSupportedException();
}
public override bool ShouldSerializeValue(object component) {
throw new NotSupportedException();
}
}
}

View File

@@ -0,0 +1,75 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Web.Resources;
using System.Globalization;
using System.Web.Caching;
using System.Web.Hosting;
namespace System.Web.DynamicData {
delegate void FileChangedCallback(string path);
class FileChangeNotifier {
private static VirtualPathProvider _vpp;
internal static VirtualPathProvider VirtualPathProvider {
private get {
if (_vpp == null) {
_vpp = HostingEnvironment.VirtualPathProvider;
}
return _vpp;
}
// For unit test purpose
set {
_vpp = value;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults",
MessageId = "System.Web.DynamicData.FileChangeNotifier",
Justification="The object deals with file change notifications and we don't need to hold on to it")]
internal static void Register(string virtualPath, FileChangedCallback onFileChanged) {
new FileChangeNotifier(virtualPath, onFileChanged);
}
private FileChangedCallback _onFileChanged;
private FileChangeNotifier(string virtualPath, FileChangedCallback onFileChanged) {
_onFileChanged = onFileChanged;
RegisterForNextNotification(virtualPath);
}
private void RegisterForNextNotification(string virtualPath) {
// Get a CacheDependency from the BuildProvider, so that we know anytime something changes
var virtualPathDependencies = new List<string>();
virtualPathDependencies.Add(virtualPath);
CacheDependency cacheDependency = VirtualPathProvider.GetCacheDependency(
virtualPath, virtualPathDependencies, DateTime.UtcNow);
// Rely on the ASP.NET cache for file change notifications, since FileSystemWatcher
// doesn't work in medium trust
HttpRuntime.Cache.Insert(virtualPath /*key*/, virtualPath /*value*/, cacheDependency,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(OnCacheItemRemoved));
}
private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) {
// We only care about dependency changes
if (reason != CacheItemRemovedReason.DependencyChanged)
return;
_onFileChanged(key);
// We need to register again to get the next notification
RegisterForNextNotification(key);
}
}
}

View File

@@ -0,0 +1,12 @@
namespace System.Web.DynamicData.Util {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// Stores per request mapping data
internal class MappingInfo {
public MetaTable Table { get; set; }
public DefaultValueMapping DefaultValueMapping { get; set; }
}
}

View File

@@ -0,0 +1,183 @@
namespace System.Web.DynamicData.Util {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.Resources;
using System.Web.UI;
using System.ComponentModel;
using IDataBoundControlInterface = System.Web.UI.WebControls.IDataBoundControl;
internal static class MetaTableHelper {
private static object s_mappingKey = new object();
internal static Dictionary<object, MappingInfo> GetMapping(HttpContextBase httpContext) {
Dictionary<object, MappingInfo> mapping = httpContext.Items[s_mappingKey] as Dictionary<object, MappingInfo>;
if (mapping == null) {
mapping = new Dictionary<object, MappingInfo>();
httpContext.Items[s_mappingKey] = mapping;
}
return mapping;
}
/// <summary>
/// Gets a table from the mapping. Does not throw.
/// </summary>
internal static MetaTable GetTableFromMapping(HttpContextBase httpContext, object control) {
IDictionary<object, MappingInfo> mapping = GetMapping(httpContext);
// don't throw if no mapping found
MappingInfo mappingInfo;
if (mapping.TryGetValue(control, out mappingInfo)) {
return mappingInfo.Table;
}
return null;
}
private static MappingInfo GetMappingInfo(object control, HttpContextBase httpContext) {
IDictionary<object, MappingInfo> mapping = GetMapping(httpContext);
MappingInfo mappingInfo;
if (!mapping.TryGetValue(control, out mappingInfo)) {
mappingInfo = new MappingInfo();
mapping[control] = mappingInfo;
}
return mappingInfo;
}
internal static void SetTableInMapping(HttpContextBase httpContext, object control, MetaTable table, IDictionary<string, object> defaultValues) {
MappingInfo mappingInfo = GetMappingInfo(control, httpContext);
mappingInfo.Table = table;
if (defaultValues != null) {
mappingInfo.DefaultValueMapping = new DefaultValueMapping(defaultValues);
}
}
internal static MetaTable GetTableWithFullFallback(IDataSource dataSource, HttpContextBase context) {
MetaTable table = GetTableFromMapping(context, dataSource);
if (table != null) {
return table;
}
IDynamicDataSource dynamicDataSource = dataSource as IDynamicDataSource;
if (dynamicDataSource != null) {
table = GetTableFromDynamicDataSource(dynamicDataSource);
if (table != null) {
return table;
}
}
table = DynamicDataRouteHandler.GetRequestMetaTable(context);
if (table != null) {
return table;
}
Control c = dataSource as Control;
string id = (c != null ? c.ID : String.Empty);
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.MetaTableHelper_CantFindTable,
id));
}
internal static MetaTable GetMetaTableFromObject(object dataSource) {
IEnumerable enumerable = dataSource as IEnumerable;
if (enumerable == null) {
return null;
}
// Use the first item to determine the type
//
foreach (object o in enumerable) {
if (o != null) {
return MetaTable.GetTable(o.GetType());
}
}
return null;
}
/// Gets a table from an IDynamicDataSource's ContextType and EntitySetName.
/// Does not throw, returns null on failure.
internal static MetaTable GetTableFromDynamicDataSource(IDynamicDataSource dynamicDataSource) {
string tableName = dynamicDataSource.EntitySetName;
Type contextType = dynamicDataSource.ContextType;
if (contextType == null || String.IsNullOrEmpty(tableName)) {
return null;
}
MetaModel model;
if (MetaModel.MetaModelManager.TryGetModel(contextType, out model)) {
Debug.Assert(model != null);
MetaTable table;
if (model.TryGetTable(tableName, out table)) {
return table;
}
}
return null;
}
internal static MetaTable FindMetaTable(Control current) {
return FindMetaTable(current, HttpContext.Current.ToWrapper());
}
internal static DefaultValueMapping GetDefaultValueMapping(Control current, HttpContextBase context) {
IDictionary<object, MappingInfo> mapping = GetMapping(context);
if (!(current is INamingContainer)) {
current = current.NamingContainer;
}
for (; current != null; current = current.NamingContainer) {
MappingInfo mappingInfo;
// If we find a mapping then return that value
if (mapping.TryGetValue(current, out mappingInfo)) {
return mappingInfo.DefaultValueMapping;
}
}
return null;
}
internal static MetaTable FindMetaTable(Control current, HttpContextBase context) {
MetaTable table = null;
// Start from the first naming container
if (!(current is INamingContainer)) {
current = current.NamingContainer;
}
for (; current != null; current = current.NamingContainer) {
// Find the first table mapped to a control
table = GetTableFromMapping(context, current);
if (table != null) {
return table;
}
IDataBoundControlInterface dataBoundControl = DataControlHelper.GetDataBoundControl(current, false /*failIfNotFound*/);
// Not a data control: continue searching
if (dataBoundControl == null) {
continue;
}
IDataSource dataSourceControl = dataBoundControl.DataSourceObject;
// Check if it's associated with a DataSource or can be retrieved from the current route
if (dataSourceControl != null) {
return GetTableWithFullFallback(dataSourceControl, context);
}
// Check if it has a datasource (i.e. not a control, but directly some data)
object dataSource = dataBoundControl.DataSource;
if (dataSource != null) {
// Try to get a MetaTable from it. If so, we're done
table = GetMetaTableFromObject(dataSource);
if (table != null) {
return table;
}
}
}
return null;
}
}
}

View File

@@ -0,0 +1,355 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.Routing;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData {
internal static class Misc {
public static HttpContextWrapper ToWrapper(this HttpContext context) {
return new HttpContextWrapper(context);
}
public static object GetRealDataItem(object dataItem) {
if (dataItem is ICustomTypeDescriptor) {
// Unwrap EF object
dataItem = ((ICustomTypeDescriptor)dataItem).GetPropertyOwner(null);
}
return dataItem;
}
// Walks the type hierachy up to endingType (assuming startingType is a subtype of starting type)
// trying to find a meta table.
public static MetaTable GetTableFromTypeHierarchy(Type entityType) {
if (entityType == null) {
throw new ArgumentNullException("entityType");
}
Type type = entityType;
while (type != null) {
MetaTable table;
if (MetaTable.TryGetTable(type, out table)) {
return table;
}
type = type.BaseType;
}
return null;
}
public static Type RemoveNullableFromType(Type type) {
return Nullable.GetUnderlyingType(type) ?? type;
}
internal static bool IsColumnInDictionary(IMetaColumn column, IDictionary<string, object> values) {
if (column == null) {
throw new ArgumentNullException("column");
}
if (values == null) {
throw new ArgumentNullException("values");
}
IMetaForeignKeyColumn foreignKeyColumn = column as IMetaForeignKeyColumn;
if (foreignKeyColumn != null) {
return foreignKeyColumn.ForeignKeyNames.All(fkName => values.ContainsKey(fkName));
}
return values.ContainsKey(column.Name);
}
internal static IDictionary<string, object> ConvertObjectToDictionary(object instance) {
if (instance == null) {
throw new ArgumentNullException("instance");
}
Dictionary<string, object> values = new Dictionary<string, object>();
var props = TypeDescriptor.GetProperties(instance);
foreach (PropertyDescriptor p in props) {
values[p.Name] = p.GetValue(instance);
}
return values;
}
public static T ChangeType<T>(object value) {
return (T)ChangeType(value, typeof(T));
}
public static object ChangeType(object value, Type type) {
if (type == null) {
throw new ArgumentNullException("type");
}
if (value == null) {
if (TypeAllowsNull(type)) {
return null;
}
return Convert.ChangeType(value, type, CultureInfo.CurrentCulture);
}
type = RemoveNullableFromType(type);
if (value.GetType() == type) {
return value;
}
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(value.GetType())) {
return converter.ConvertFrom(value);
}
TypeConverter otherConverter = TypeDescriptor.GetConverter(value.GetType());
if (otherConverter.CanConvertTo(type)) {
return otherConverter.ConvertTo(value, type);
}
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture,
DynamicDataResources.Misc_CannotConvertType, value.GetType(), type));
}
internal static bool TypeAllowsNull(Type type) {
return Nullable.GetUnderlyingType(type) != null || !type.IsValueType;
}
public static ContainerType FindContainerType(Control control) {
if (control == null) {
throw new ArgumentNullException("control");
}
Control container = control;
// Walk up NamingContainers until we find one of the DataBound control interfaces
while (container != null) {
if (container is IDataBoundItemControl) {
return ContainerType.Item;
}
else if (container is IDataBoundListControl || container is Repeater) {
return ContainerType.List;
}
container = container.NamingContainer;
}
// Default container type is a list if none of the known
// interfaces are found
return ContainerType.List;
}
public static IOrderedDictionary GetEnumNamesAndValues(Type enumType) {
Debug.Assert(enumType != null);
Debug.Assert(enumType.IsEnum);
OrderedDictionary result = new OrderedDictionary();
var enumEntries = from e in Enum.GetValues(enumType).OfType<object>()
select new EnumEntry {
//
Name = Enum.GetName(enumType, e),
UnderlyingValue = GetUnderlyingTypeValue(enumType, e)
};
foreach (var entry in enumEntries.OrderBy(e => e.UnderlyingValue)) {
result.Add(entry.Name, entry.UnderlyingValue.ToString());
}
return result;
}
private struct EnumEntry {
public string Name { get; set; }
public object UnderlyingValue { get; set; }
}
public static object GetUnderlyingTypeValue(Type enumType, object enumValue) {
return Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture);
}
public static string GetUnderlyingTypeValueString(Type enumType, object enumValue) {
return GetUnderlyingTypeValue(enumType, enumValue).ToString();
}
public static string PersistListToCommaSeparatedString(IList<object> list) {
// Special case empty and single lists
if (list == null || list.Count == 0)
return String.Empty;
if (list.Count == 1) {
return list[0] == null ? String.Empty : list[0].ToString().TrimEnd();
}
var builder = new StringBuilder();
bool first = true;
bool hasNonNullItem = false;
foreach (object o in list) {
if (!first) {
builder.Append(",");
}
if (o != null) {
//
builder.Append(o.ToString().TrimEnd());
hasNonNullItem = true;
}
first = false;
}
// If all the parts are null, return empty string instead of the comma separated list
if (!hasNonNullItem)
return String.Empty;
return builder.ToString();
}
//
public static object[] GetKeyValues(IList<MetaColumn> keyMembers, object entity) {
object[] values = new object[keyMembers.Count];
int index = 0;
foreach (MetaColumn pkMember in keyMembers) {
values[index++] = DataBinder.GetPropertyValue(entity, pkMember.Name);
}
return values;
}
public static string[] ParseCommaSeparatedString(string stringList) {
//
return stringList.Split(',');
}
public static IQueryable BuildSortQueryable(IQueryable query, IMetaTable table) {
IMetaColumn sortColumn = table.SortColumn;
if (sortColumn.IsCustomProperty) {
// An extra property can't be optimized on server
//
var data = query.OfType<object>().AsEnumerable();
Func<object, object> lambda = row => DataBinder.GetPropertyValue(row, sortColumn.Name);
if (table.SortDescending) {
query = data.OrderByDescending<object, object>(lambda).AsQueryable();
}
else {
query = data.OrderBy<object, object>(lambda).AsQueryable();
}
}
else {
// Build custom expression to optimize sorting on server
//
var parameter = Expression.Parameter(query.ElementType, "row");
LambdaExpression lambda = null;
IMetaForeignKeyColumn foreignKeyColumn = sortColumn as IMetaForeignKeyColumn;
if (foreignKeyColumn != null) {
// e.g. product => product.Category.CategoryName
var foreignKeySortColumn = foreignKeyColumn.ParentTable.SortColumn;
lambda = Expression.Lambda(Expression.Property(Expression.Property(parameter, sortColumn.Name), foreignKeySortColumn.Name), parameter);
}
else {
// e.g. product => product.ProductName
lambda = Expression.Lambda(Expression.Property(parameter, sortColumn.Name), parameter);
}
string ordering = table.SortDescending ? "OrderByDescending" : "OrderBy";
var expression = Expression.Call(typeof(Queryable), ordering, new Type[] { query.ElementType, lambda.Body.Type }, query.Expression, lambda);
query = query.Provider.CreateQuery(expression);
}
return query;
}
// Fill a ListItemCollection with all the entries from a table
public static void FillListItemCollection(IMetaTable table, ListItemCollection listItemCollection) {
foreach (var o in table.GetQuery()) {
string text = table.GetDisplayString(o);
string value = table.GetPrimaryKeyString(o);
listItemCollection.Add(new ListItem(text, value.TrimEnd()));
}
}
internal static void ExtractValuesFromBindableControls(IOrderedDictionary dictionary, Control container) {
IBindableControl bindableControl = container as IBindableControl;
if (bindableControl != null) {
bindableControl.ExtractValues(dictionary);
}
foreach (Control childControl in container.Controls) {
ExtractValuesFromBindableControls(dictionary, childControl);
}
}
/// <devdoc>
/// Walks up the stack of NamingContainers starting at 'control' to find a control with the ID 'controlID'.
/// Copied from DataBoundControlHelper.FindControl (System.Web)
/// </devdoc>
public static Control FindControl(Control control, string controlID) {
Debug.Assert(control != null, "control should not be null");
//Debug.Assert(!String.IsNullOrEmpty(controlID), "controlID should not be empty");
Control currentContainer = control;
Control foundControl = null;
if (control == control.Page) {
// If we get to the Page itself while we're walking up the
// hierarchy, just return whatever item we find (if anything)
// since we can't walk any higher.
return control.FindControl(controlID);
}
while (foundControl == null && currentContainer != control.Page) {
currentContainer = currentContainer.NamingContainer;
if (currentContainer == null) {
throw new HttpException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.Misc_NoNamingContainer,
control.GetType().Name, control.ID));
}
foundControl = currentContainer.FindControl(controlID);
}
return foundControl;
}
public static string GetRouteValue(string key) {
RequestContext requestContext = DynamicDataRouteHandler.GetRequestContext(HttpContext.Current);
object value;
if (!requestContext.RouteData.Values.TryGetValue(key, out value)) {
return null;
}
return value as string;
}
public static string SanitizeQueryStringValue(object value) {
if (value == null)
return null;
string strValue = value.ToString();
// Trim trailing spaces, as they are typically meaningless, and make the url look ugly
return strValue.TrimEnd();
}
internal static long CombineHashCodes(object o1, object o2) {
// Start with a seed (obtained from String.GetHashCode implementation)
long combinedHash = 5381;
combinedHash = AddHashCode(combinedHash, o1);
combinedHash = AddHashCode(combinedHash, o2);
return combinedHash;
}
// Return a single hash code for 3 objects
internal static long CombineHashCodes(object o1, object o2, object o3) {
// Start with a seed (obtained from String.GetHashCode implementation)
long combinedHash = 5381;
combinedHash = AddHashCode(combinedHash, o1);
combinedHash = AddHashCode(combinedHash, o2);
combinedHash = AddHashCode(combinedHash, o3);
return combinedHash;
}
private static long AddHashCode(long currentHash, object o) {
if (o == null)
return currentHash;
return ((currentHash << 5) + currentHash) ^ o.GetHashCode();
}
}
}

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData.Util {
internal class RouteParametersHelper {
private static Parameter CreateParameter(string name, string value, MetaColumn configurationColumn) {
var param = new Parameter() {
Name = name,
DefaultValue = value
};
DataSourceUtil.SetParameterTypeCodeAndDbType(param, configurationColumn);
return param;
}
internal static IEnumerable<Parameter> GetColumnParameters(MetaTable table, string columnName) {
if (String.IsNullOrEmpty(columnName)) {
// If no column is specified, we're setting the primary key from the query string
return GetPrimaryKeyParameters(table);
} else {
var column = table.GetColumn(columnName);
var fkColumn = column as MetaForeignKeyColumn;
if (fkColumn != null) {
// Handle the case where we're setting one of our foreign keys from the query string
return GetForeignKeyParameters(fkColumn);
} else {
// Handle other columns (e.g. booleans)
return GetRegularColumnParameters(column);
}
}
}
internal static IEnumerable<Parameter> GetForeignKeyParameters(MetaForeignKeyColumn fkColumn) {
Debug.Assert(fkColumn.ForeignKeyNames.Count == fkColumn.ParentTable.PrimaryKeyColumns.Count);
var result = new List<Parameter>();
for (int i = 0; i < fkColumn.ForeignKeyNames.Count; i++) {
string name = fkColumn.ForeignKeyNames[i];
string value = Misc.GetRouteValue(name);
MetaColumn parentTablePKColumn = fkColumn.ParentTable.PrimaryKeyColumns[i];
var param = CreateParameter(name, value, parentTablePKColumn);
result.Add(param);
}
return result;
}
internal static IEnumerable<Parameter> GetPrimaryKeyParameters(MetaTable table) {
var result = new List<Parameter>();
foreach (var primaryKeyColumn in table.PrimaryKeyColumns) {
string name = primaryKeyColumn.Name;
string value = Misc.GetRouteValue(name);
var param = CreateParameter(name, value, primaryKeyColumn);
result.Add(param);
}
return result;
}
internal static IEnumerable<Parameter> GetRegularColumnParameters(MetaColumn column) {
// Handle other columns (e.g. booleans)
string name = column.Name;
string value = Misc.GetRouteValue(name);
var param = CreateParameter(name, value, column);
return new List<Parameter>() { param };
}
}
}

View File

@@ -0,0 +1,153 @@
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Resources;
namespace System.Web.DynamicData {
internal class TemplateFactory {
// Use Hashtable instead of Dictionary<,> because it is more thread safe
private Hashtable _fieldTemplateVirtualPathCache = new Hashtable();
private string _defaultLocation;
private string _templateFolderVirtualPath;
internal MetaModel Model { get; set; }
private bool _needToResolveVirtualPath;
private bool _trackFolderChanges;
private bool _registeredForChangeNotifications;
private VirtualPathProvider _vpp;
private bool _usingCustomVpp;
internal TemplateFactory(string defaultLocation)
: this(defaultLocation, true) {
}
internal TemplateFactory(string defaultLocation, bool trackFolderChanges) {
Debug.Assert(!String.IsNullOrEmpty(defaultLocation));
_defaultLocation = defaultLocation;
_trackFolderChanges = trackFolderChanges;
}
internal string TemplateFolderVirtualPath {
get {
if (_templateFolderVirtualPath == null) {
// If not set, set its default location
TemplateFolderVirtualPath = _defaultLocation;
}
if (_needToResolveVirtualPath) {
// Make sure it ends with a slash
_templateFolderVirtualPath = VirtualPathUtility.AppendTrailingSlash(_templateFolderVirtualPath);
// If it's relative, make it relative to the Model's path
// Note can be null under Unit Testing
if (Model != null) {
_templateFolderVirtualPath = VirtualPathUtility.Combine(Model.DynamicDataFolderVirtualPath, _templateFolderVirtualPath);
}
_needToResolveVirtualPath = false;
}
return _templateFolderVirtualPath;
}
set {
_templateFolderVirtualPath = value;
// Make sure we register for change notifications, since we just got a new path
_registeredForChangeNotifications = false;
// It may be relative and need resolution, but let's not do it until we need it
_needToResolveVirtualPath = true;
}
}
internal VirtualPathProvider VirtualPathProvider {
get {
if (_vpp == null) {
_vpp = HostingEnvironment.VirtualPathProvider;
}
return _vpp;
}
set {
_vpp = value;
_usingCustomVpp = value != null;
}
}
internal string GetTemplatePath(long cacheKey, Func<string> templatePathFactoryFunction) {
// Check if we already have it cached
string virtualPath = this[cacheKey];
// null is a valid value, so we also need to check whether the key exists
if (virtualPath == null && !ContainsKey(cacheKey)) {
// It's not cached, so compute it and cache it. Make sure multiple writers are serialized
virtualPath = templatePathFactoryFunction();
this[cacheKey] = virtualPath;
}
return virtualPath;
}
private string this[long cacheKey] {
get {
EnsureRegisteredForChangeNotifications();
return (string)_fieldTemplateVirtualPathCache[cacheKey];
}
set {
EnsureRegisteredForChangeNotifications();
lock (_fieldTemplateVirtualPathCache) {
_fieldTemplateVirtualPathCache[cacheKey] = value;
}
}
}
private bool ContainsKey(long cacheKey) {
EnsureRegisteredForChangeNotifications();
return _fieldTemplateVirtualPathCache.ContainsKey(cacheKey);
}
private void EnsureRegisteredForChangeNotifications() {
if (!_trackFolderChanges) {
return;
}
if (!_registeredForChangeNotifications) {
lock (this) {
if (!_registeredForChangeNotifications) {
// Make sure the folder exists
if (!VirtualPathProvider.DirectoryExists(TemplateFolderVirtualPath)) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture,
DynamicDataResources.FieldTemplateFactory_FolderNotFound,
TemplateFolderVirtualPath));
}
// Register for notifications if anything in that folder changes
FileChangeNotifier.Register(TemplateFolderVirtualPath, delegate(string path) {
// Something has changed, so clear our cache
lock (_fieldTemplateVirtualPathCache) {
_fieldTemplateVirtualPathCache.Clear();
}
});
_registeredForChangeNotifications = true;
}
}
}
}
internal bool FileExists(string virtualPath) {
if (_usingCustomVpp) {
// for unit testing
return VirtualPathProvider.FileExists(virtualPath);
} else {
// Use GetObjectFactory instead of GetCompiledType because it will not throw, which improves the debugging experience
return BuildManager.GetObjectFactory(virtualPath, /* throwIfNotFound */ false) != null;
}
}
}
}