You've already forked linux-packaging-mono
Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
125
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/AssociationProvider.cs
vendored
Normal file
125
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/AssociationProvider.cs
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Web.Resources;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
/// <summary>
|
||||
/// Specifies the association cardinality
|
||||
/// </summary>
|
||||
public enum AssociationDirection {
|
||||
/// <summary>
|
||||
/// 1-1
|
||||
/// </summary>
|
||||
OneToOne,
|
||||
/// <summary>
|
||||
/// one to many
|
||||
/// </summary>
|
||||
OneToMany,
|
||||
/// <summary>
|
||||
/// many to one
|
||||
/// </summary>
|
||||
ManyToOne,
|
||||
/// <summary>
|
||||
/// many to many
|
||||
/// </summary>
|
||||
ManyToMany
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base provider class for associations between columns
|
||||
/// Each provider type (e.g. Linq To Sql, Entity Framework, 3rd party) extends this class.
|
||||
/// </summary>
|
||||
public abstract class AssociationProvider {
|
||||
private TableProvider _toTable;
|
||||
|
||||
/// <summary>
|
||||
/// The type of association
|
||||
/// </summary>
|
||||
public virtual AssociationDirection Direction { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source column of the association
|
||||
/// </summary>
|
||||
public virtual ColumnProvider FromColumn { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The destination table of the association
|
||||
/// </summary>
|
||||
public virtual TableProvider ToTable {
|
||||
get {
|
||||
if (_toTable != null) {
|
||||
return _toTable;
|
||||
}
|
||||
|
||||
if (ToColumn != null) {
|
||||
return ToColumn.Table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
protected set {
|
||||
_toTable = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The destination column of the association
|
||||
/// </summary>
|
||||
public virtual ColumnProvider ToColumn { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the From Column part of the primary key of its table
|
||||
/// e.g. Order and Product are PKs in the Order_Details table
|
||||
/// </summary>
|
||||
public virtual bool IsPrimaryKeyInThisTable { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The names of the underlying foreign keys that make up this association
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification="It's a readonly collection, so the warning is incorrect")]
|
||||
public virtual ReadOnlyCollection<string> ForeignKeyNames { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representing the sort expression that would be used for
|
||||
/// sorting the column represented by this association. The parameter is the
|
||||
/// property of the strongly typed entity used as the sort key for that entity.
|
||||
/// For example, assume that this association represents the Category column
|
||||
/// in the Products table. The sortColumn paramater is "CategoryName",
|
||||
/// meaning that this method is being asked to return the sort expression for
|
||||
/// sorting the Category column by the CategoryName property of the Category entity.
|
||||
/// The result sort expression would be "Category.CategoryName".
|
||||
/// The result of this method should be affected by whether the underlying data
|
||||
/// model is capable of sorting the entity by the given sort column (see
|
||||
/// ColumnProvider.IsSortable). The method can return a null value to indicate
|
||||
/// that sorting is not supported.
|
||||
/// </summary>
|
||||
/// <param name="sortColumn">the column to sort the entity by</param>
|
||||
/// <returns>the sort expression string, or null if sort is not supported for the
|
||||
/// given sort column</returns>
|
||||
public virtual string GetSortExpression(ColumnProvider sortColumn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string GetSortExpression(ColumnProvider sortColumn, string format) {
|
||||
if (Direction == AssociationDirection.OneToMany || Direction == AssociationDirection.ManyToMany) {
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
|
||||
DynamicDataResources.AssociationProvider_DirectionDoesNotSupportSorting,
|
||||
Direction));
|
||||
}
|
||||
|
||||
if (sortColumn == null) {
|
||||
throw new ArgumentNullException("sortColumn");
|
||||
}
|
||||
|
||||
if (!ToTable.Columns.Contains(sortColumn)) {
|
||||
throw new ArgumentException(DynamicDataResources.AssociationProvider_SortColumnDoesNotBelongToEndTable, "sortColumn");
|
||||
}
|
||||
|
||||
if (sortColumn.IsSortable) {
|
||||
return String.Format(CultureInfo.InvariantCulture, format, FromColumn.Name, sortColumn.Name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
157
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/ColumnProvider.cs
vendored
Normal file
157
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/ColumnProvider.cs
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Web.DynamicData;
|
||||
using System.Web.DynamicData.Util;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
/// <summary>
|
||||
/// Base provider class for columns.
|
||||
/// Each provider type (e.g. Linq To Sql, Entity Framework, 3rd party) extends this class.
|
||||
/// </summary>
|
||||
public abstract class ColumnProvider {
|
||||
private bool? _isReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="table">the table this column belongs to</param>
|
||||
protected ColumnProvider(TableProvider table) {
|
||||
if (table == null) {
|
||||
throw new ArgumentNullException("table");
|
||||
}
|
||||
|
||||
Table = table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// readable representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public override string ToString() {
|
||||
// To help identifying objects in debugger
|
||||
return Name ?? base.ToString();
|
||||
}
|
||||
|
||||
internal virtual PropertyDescriptor PropertyDescriptor {
|
||||
get {
|
||||
return Table.GetTypeDescriptor().GetProperties().Find(Name, true/*ignoreCase*/);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual AttributeCollection Attributes {
|
||||
get {
|
||||
var propertyDescriptor = PropertyDescriptor;
|
||||
var attributes = propertyDescriptor != null ? propertyDescriptor.Attributes : AttributeCollection.Empty;
|
||||
return AddDefaultAttributes(this, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
protected static AttributeCollection AddDefaultAttributes(ColumnProvider columnProvider, AttributeCollection attributes) {
|
||||
List<Attribute> extraAttributes = new List<Attribute>();
|
||||
|
||||
// If there is no required attribute and the Provider says required, add one
|
||||
var requiredAttribute = attributes.FirstOrDefault<RequiredAttribute>();
|
||||
if (requiredAttribute == null && !columnProvider.Nullable) {
|
||||
extraAttributes.Add(new RequiredAttribute());
|
||||
}
|
||||
|
||||
// If there is no StringLength attribute and it's a string, add one
|
||||
var stringLengthAttribute = attributes.FirstOrDefault<StringLengthAttribute>();
|
||||
int maxLength = columnProvider.MaxLength;
|
||||
if (stringLengthAttribute == null && columnProvider.ColumnType == typeof(String) && maxLength > 0) {
|
||||
extraAttributes.Add(new StringLengthAttribute(maxLength));
|
||||
}
|
||||
|
||||
// If we need any extra attributes, create a new collection
|
||||
if (extraAttributes.Count > 0) {
|
||||
attributes = AttributeCollection.FromExisting(attributes, extraAttributes.ToArray());
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the column
|
||||
/// </summary>
|
||||
public virtual string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The CLR type of the column
|
||||
/// </summary>
|
||||
public virtual Type ColumnType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this column a primary key in its table
|
||||
/// </summary>
|
||||
public virtual bool IsPrimaryKey { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies if this column is read only
|
||||
/// </summary>
|
||||
public virtual bool IsReadOnly {
|
||||
get {
|
||||
if (_isReadOnly == null) {
|
||||
var propertyDescriptor = PropertyDescriptor;
|
||||
_isReadOnly = propertyDescriptor != null ? propertyDescriptor.IsReadOnly : false;
|
||||
}
|
||||
return _isReadOnly.Value;
|
||||
}
|
||||
protected set {
|
||||
_isReadOnly = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is it a database generated column
|
||||
/// </summary>
|
||||
public virtual bool IsGenerated { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the underlying model supports sorting of the table on this column
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sortable", Justification="It's a valid word")]
|
||||
public virtual bool IsSortable { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximun length allowed for this column (applies to string columns)
|
||||
/// </summary>
|
||||
public virtual int MaxLength { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does it allow null values (meaning it is not required)
|
||||
/// </summary>
|
||||
public virtual bool Nullable { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// meant to indicate that a member is an extra property that was declared in a partial class
|
||||
/// </summary>
|
||||
public virtual bool IsCustomProperty { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the column represents and association with anther table, this returns the association information.
|
||||
/// Otherwise, null is returned.
|
||||
/// </summary>
|
||||
public virtual AssociationProvider Association { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The table that this column belongs to
|
||||
/// </summary>
|
||||
public TableProvider Table { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The PropertyInfo of the property that represents this column on the entity type
|
||||
/// </summary>
|
||||
public virtual PropertyInfo EntityTypeProperty { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is set for columns that are part of a foreign key. Note that it is NOT set for
|
||||
/// the strongly typed entity ref columns (though those columns 'use' one or more columns
|
||||
/// where IsForeignKeyComponent is set).
|
||||
/// </summary>
|
||||
public virtual bool IsForeignKeyComponent { get; protected set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class DLinqAssociationProvider : AssociationProvider {
|
||||
|
||||
public DLinqAssociationProvider(DLinqColumnProvider column) {
|
||||
FromColumn = column;
|
||||
|
||||
MetaAssociation association = column.Member.Association;
|
||||
|
||||
SetOtherEndOfAssociation(association);
|
||||
|
||||
SetDirection(association);
|
||||
Debug.Assert(Direction != AssociationDirection.ManyToMany, "Many to Many is not supported by Linq to SQL");
|
||||
|
||||
SetAssociationKeyInfo(association);
|
||||
}
|
||||
|
||||
private void SetAssociationKeyInfo(MetaAssociation association) {
|
||||
DLinqColumnProvider column = (DLinqColumnProvider)FromColumn;
|
||||
|
||||
List<string> foreignKeyNames = new List<string>();
|
||||
|
||||
int count = column.Member.Association.ThisKey.Count;
|
||||
for (int i = 0; i < count; i++) {
|
||||
MetaDataMember thisKeyMetaDataMember = column.Member.Association.ThisKey[i];
|
||||
MetaDataMember otherKeyMetaDataMember = column.Member.Association.OtherKey[i];
|
||||
|
||||
DLinqColumnProvider thisEntityMemberComponent = FindColumn(column.Table, thisKeyMetaDataMember.Name);
|
||||
|
||||
if (ShouldRemoveThisAssociation(association)) {
|
||||
column.ShouldRemove = true;
|
||||
return;
|
||||
}
|
||||
|
||||
foreignKeyNames.Add(thisEntityMemberComponent.Name);
|
||||
|
||||
if (thisEntityMemberComponent.IsPrimaryKey) {
|
||||
IsPrimaryKeyInThisTable = true;
|
||||
}
|
||||
if (association.IsForeignKey) {
|
||||
thisEntityMemberComponent.IsForeignKeyComponent = true;
|
||||
}
|
||||
}
|
||||
|
||||
ForeignKeyNames = new ReadOnlyCollection<string>(foreignKeyNames);
|
||||
}
|
||||
|
||||
private bool ShouldRemoveThisAssociation(MetaAssociation association) {
|
||||
if (Direction == AssociationDirection.ManyToOne && !association.OtherKeyIsPrimaryKey) {
|
||||
return true;
|
||||
}
|
||||
if (Direction == AssociationDirection.OneToMany && !association.ThisKeyIsPrimaryKey) {
|
||||
return true;
|
||||
}
|
||||
if (Direction == AssociationDirection.OneToOne) {
|
||||
if (!association.IsForeignKey && !association.ThisKeyIsPrimaryKey) {
|
||||
return true;
|
||||
}
|
||||
if (association.IsForeignKey && !association.OtherKeyIsPrimaryKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetOtherEndOfAssociation(MetaAssociation association) {
|
||||
DLinqTableProvider entityMemberParentEntity = (DLinqTableProvider)FromColumn.Table;
|
||||
DLinqDataModelProvider parentEntityDataContext = (DLinqDataModelProvider)entityMemberParentEntity.DataModel;
|
||||
if (association.OtherMember != null) {
|
||||
ToColumn = parentEntityDataContext.ColumnLookup[(PropertyInfo)association.OtherMember.Member];
|
||||
} else {
|
||||
ToTable = ((DLinqDataModelProvider)FromColumn.Table.DataModel).DLinqTables.Single(tp => tp.EntityType == association.OtherType.Type);
|
||||
}
|
||||
}
|
||||
|
||||
private static DLinqColumnProvider FindColumn(TableProvider table, String columnName) {
|
||||
//
|
||||
return (DLinqColumnProvider)table.Columns.First(member => member.Name.Equals(columnName));
|
||||
}
|
||||
|
||||
private void SetDirection(MetaAssociation association) {
|
||||
if (association.IsMany) {
|
||||
Direction = AssociationDirection.OneToMany;
|
||||
} else if (association.OtherMember == null || association.OtherMember.Association.IsMany) {
|
||||
// there might not be the other member if this is a one-sided association
|
||||
Direction = AssociationDirection.ManyToOne;
|
||||
} else {
|
||||
Direction = AssociationDirection.OneToOne;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetSortExpression(ColumnProvider sortColumn) {
|
||||
return GetSortExpression(sortColumn, "{0}.{1}");
|
||||
}
|
||||
}
|
||||
}
|
158
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DLinqColumnProvider.cs
vendored
Normal file
158
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DLinqColumnProvider.cs
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data.Linq;
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class DLinqColumnProvider : ColumnProvider {
|
||||
private static Regex s_varCharRegEx = new Regex(@"N?(?:Var)?Char\(([0-9]+)\)", RegexOptions.IgnoreCase); // accepts char, nchar, varchar, and nvarchar
|
||||
|
||||
private AttributeCollection _attributes;
|
||||
private AssociationProvider _association;
|
||||
private bool _isAssociation;
|
||||
|
||||
public DLinqColumnProvider(DLinqTableProvider table, MetaDataMember member)
|
||||
: base(table) {
|
||||
Member = member;
|
||||
Name = member.Name;
|
||||
ColumnType = GetMemberType(member);
|
||||
IsPrimaryKey = member.IsPrimaryKey;
|
||||
IsGenerated = member.IsDbGenerated;
|
||||
_isAssociation = member.IsAssociation;
|
||||
IsCustomProperty = !member.IsAssociation && Member.DbType == null;
|
||||
Nullable = Member.IsAssociation ? Member.Association.IsNullable : Member.CanBeNull;
|
||||
MaxLength = ProcessMaxLength(ColumnType, Member.DbType);
|
||||
IsSortable = ProcessIsSortable(ColumnType, Member.DbType);
|
||||
}
|
||||
|
||||
public override AttributeCollection Attributes {
|
||||
get {
|
||||
if (!Member.IsDiscriminator)
|
||||
return base.Attributes;
|
||||
|
||||
if (_attributes == null) {
|
||||
List<Attribute> newAttributes = new List<Attribute>();
|
||||
bool foundScaffoldAttribute = false;
|
||||
|
||||
foreach (Attribute attr in base.Attributes) {
|
||||
if (attr is ScaffoldColumnAttribute) {
|
||||
foundScaffoldAttribute = true;
|
||||
break;
|
||||
}
|
||||
newAttributes.Add(attr);
|
||||
}
|
||||
|
||||
if (foundScaffoldAttribute)
|
||||
_attributes = base.Attributes;
|
||||
else {
|
||||
newAttributes.Add(new ScaffoldColumnAttribute(false));
|
||||
_attributes = new AttributeCollection(newAttributes.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
return _attributes;
|
||||
}
|
||||
}
|
||||
|
||||
// internal to facilitate unit testing
|
||||
internal static int ProcessMaxLength(Type memberType, String dbType) {
|
||||
// Only strings and chars that come in from a database have max lengths
|
||||
if (dbType == null || (memberType != typeof(string) && Misc.RemoveNullableFromType(memberType) != typeof(char)))
|
||||
return 0;
|
||||
|
||||
if (dbType.StartsWith("NText", StringComparison.OrdinalIgnoreCase)) {
|
||||
return Int32.MaxValue >> 1; // see sql server 2005 spec for ntext
|
||||
}
|
||||
|
||||
if (dbType.StartsWith("Text", StringComparison.OrdinalIgnoreCase)) {
|
||||
return Int32.MaxValue; // see sql server 2005 spec for text
|
||||
}
|
||||
|
||||
if (dbType.StartsWith("NVarChar(MAX)", StringComparison.OrdinalIgnoreCase)) {
|
||||
return (Int32.MaxValue >> 1) - 2; // see sql server 2005 spec for nvarchar
|
||||
}
|
||||
|
||||
if (dbType.StartsWith("VarChar(MAX)", StringComparison.OrdinalIgnoreCase)) {
|
||||
return Int32.MaxValue - 2; // see sql server 2005 spec for varchar
|
||||
}
|
||||
|
||||
Match m = s_varCharRegEx.Match(dbType);
|
||||
if (m.Success) {
|
||||
return Int32.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal static bool ProcessIsSortable(Type memberType, String dbType) {
|
||||
if (dbType == null)
|
||||
return false;
|
||||
|
||||
if (memberType == typeof(string) &&
|
||||
(dbType.StartsWith("Text", StringComparison.OrdinalIgnoreCase)
|
||||
|| dbType.StartsWith("NText", StringComparison.OrdinalIgnoreCase))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memberType == typeof(Binary) && dbType.StartsWith("Image", StringComparison.OrdinalIgnoreCase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memberType == typeof(XElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal MetaDataMember Member {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal void Initialize() {
|
||||
if (_isAssociation && _association == null) {
|
||||
_association = new DLinqAssociationProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ShouldRemove { get; set; }
|
||||
|
||||
private static Type GetMemberType(MetaDataMember member) {
|
||||
Type type = member.Type;
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EntitySet<>)) {
|
||||
return type.GetGenericArguments()[0];
|
||||
}
|
||||
else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
#region IEntityMember Members
|
||||
|
||||
public override PropertyInfo EntityTypeProperty {
|
||||
get { return (PropertyInfo)Member.Member; }
|
||||
}
|
||||
|
||||
public override AssociationProvider Association {
|
||||
get {
|
||||
Initialize();
|
||||
|
||||
return _association;
|
||||
}
|
||||
}
|
||||
|
||||
internal new bool IsForeignKeyComponent {
|
||||
set {
|
||||
base.IsForeignKeyComponent = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Linq;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using LinqMetaTable = System.Data.Linq.Mapping.MetaTable;
|
||||
using LinqMetaType = System.Data.Linq.Mapping.MetaType;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class DLinqDataModelProvider : DataModelProvider {
|
||||
private ReadOnlyCollection<TableProvider> _roTables;
|
||||
private Dictionary<PropertyInfo, DLinqColumnProvider> _columnLookup = new Dictionary<PropertyInfo, DLinqColumnProvider>();
|
||||
|
||||
private Func<object> ContextFactory { get; set; }
|
||||
|
||||
public DLinqDataModelProvider(object contextInstance, Func<object> contextFactory) {
|
||||
ContextFactory = contextFactory;
|
||||
|
||||
DataContext context = (DataContext)contextInstance ?? (DataContext)CreateContext();
|
||||
ContextType = context.GetType();
|
||||
|
||||
DLinqTables = new List<TableProvider>();
|
||||
foreach (PropertyInfo prop in ContextType.GetProperties()) {
|
||||
Type entityType = GetEntityType(prop);
|
||||
|
||||
if (entityType != null) {
|
||||
LinqMetaTable table = GetLinqTable(context, entityType);
|
||||
ProcessTable(table, table.RowType, prop.Name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
DLinqTables.ForEach(t => ((DLinqTableProvider)t).Initialize());
|
||||
|
||||
_roTables = new ReadOnlyCollection<TableProvider>(DLinqTables);
|
||||
}
|
||||
|
||||
private LinqMetaTable GetLinqTable(DataContext context, Type entityType) {
|
||||
return context.Mapping.GetTables().First(t => t.RowType.Type == entityType);
|
||||
}
|
||||
|
||||
private Type GetEntityType(PropertyInfo prop) {
|
||||
//
|
||||
if (prop.PropertyType.IsGenericType &&
|
||||
prop.PropertyType.GetGenericTypeDefinition() == typeof(Table<>))
|
||||
return prop.PropertyType.GetGenericArguments()[0];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ProcessTable(LinqMetaTable table, LinqMetaType rowType, string name, PropertyInfo prop) {
|
||||
DLinqTables.Add(new DLinqTableProvider(this, rowType, name, prop));
|
||||
|
||||
foreach (LinqMetaType derivedType in rowType.DerivedTypes)
|
||||
ProcessTable(table, derivedType, derivedType.Name, prop);
|
||||
}
|
||||
|
||||
internal Dictionary<PropertyInfo, DLinqColumnProvider> ColumnLookup {
|
||||
get {
|
||||
return _columnLookup;
|
||||
}
|
||||
}
|
||||
|
||||
internal List<TableProvider> DLinqTables { get; private set; }
|
||||
|
||||
public override object CreateContext() {
|
||||
return ContextFactory();
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<TableProvider> Tables {
|
||||
get {
|
||||
return _roTables;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DLinqTableProvider.cs
vendored
Normal file
85
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DLinqTableProvider.cs
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class DLinqTableProvider : TableProvider {
|
||||
private ReadOnlyCollection<ColumnProvider> _roColumns;
|
||||
private List<ColumnProvider> _columns;
|
||||
private MetaType _rowType;
|
||||
private PropertyInfo _prop;
|
||||
|
||||
public DLinqTableProvider(DLinqDataModelProvider dataModel, MetaType rowType, string name, PropertyInfo prop)
|
||||
: base(dataModel) {
|
||||
_prop = prop;
|
||||
_rowType = rowType;
|
||||
Name = name;
|
||||
DataContextPropertyName = prop.Name;
|
||||
EntityType = rowType.Type;
|
||||
ParentEntityType = rowType.InheritanceBase != null ? rowType.InheritanceBase.Type : null;
|
||||
RootEntityType = rowType.Table.RowType.Type;
|
||||
|
||||
_columns = new List<ColumnProvider>();
|
||||
var members = new List<MetaDataMember>(rowType.DataMembers);
|
||||
|
||||
// Add in base-class-first order (not the typical derived-class-first order)
|
||||
foreach (PropertyInfo propInfo in GetOrderedProperties(rowType.Type)) {
|
||||
MetaDataMember member = members.FirstOrDefault(m => m.Member.Name == propInfo.Name);
|
||||
if (member != null) {
|
||||
AddColumn(dataModel, member, propInfo);
|
||||
members.Remove(member);
|
||||
}
|
||||
}
|
||||
|
||||
// Anything we might've missed, tack it onto the end
|
||||
foreach (MetaDataMember member in members) {
|
||||
AddColumn(dataModel, member, (PropertyInfo)member.Member);
|
||||
}
|
||||
|
||||
_roColumns = new ReadOnlyCollection<ColumnProvider>(_columns);
|
||||
}
|
||||
|
||||
private void AddColumn(DLinqDataModelProvider dataModel, MetaDataMember member, PropertyInfo propInfo) {
|
||||
var publicGetAccessor = propInfo.GetGetMethod();
|
||||
if (publicGetAccessor == null) {
|
||||
// the property at least needs to have a public getter, otherwise databinding will not work
|
||||
return;
|
||||
}
|
||||
|
||||
DLinqColumnProvider column = new DLinqColumnProvider(this, member);
|
||||
_columns.Add(column);
|
||||
|
||||
if (!dataModel.ColumnLookup.ContainsKey(propInfo))
|
||||
dataModel.ColumnLookup[propInfo] = column;
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyInfo> GetOrderedProperties(Type type) {
|
||||
if (type == null)
|
||||
return new PropertyInfo[0];
|
||||
PropertyInfo[] props = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
|
||||
return GetOrderedProperties(type.BaseType).Concat(props);
|
||||
}
|
||||
|
||||
internal void Initialize() {
|
||||
_columns.ForEach(c => ((DLinqColumnProvider)c).Initialize());
|
||||
_columns.RemoveAll(c => ((DLinqColumnProvider)c).ShouldRemove);
|
||||
}
|
||||
|
||||
#region IEntity Members
|
||||
|
||||
public override IQueryable GetQuery(object context) {
|
||||
return (IQueryable)_prop.GetValue(context, null);
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<ColumnProvider> Columns {
|
||||
get {
|
||||
return _roColumns;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
24
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DataModelProvider.cs
vendored
Normal file
24
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/DataModelProvider.cs
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
/// <summary>
|
||||
/// Base data model provider class
|
||||
/// Each provider type (e.g. Linq To Sql, Entity Framework, 3rd party) extends this class.
|
||||
/// </summary>
|
||||
public abstract class DataModelProvider {
|
||||
/// <summary>
|
||||
/// The list of tables exposed by this data model
|
||||
/// </summary>
|
||||
public abstract ReadOnlyCollection<TableProvider> Tables { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the data context
|
||||
/// </summary>
|
||||
public virtual Type ContextType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the data context
|
||||
/// </summary>
|
||||
public abstract object CreateContext();
|
||||
}
|
||||
}
|
129
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFAssociationProvider.cs
vendored
Normal file
129
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFAssociationProvider.cs
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class EFAssociationProvider : AssociationProvider {
|
||||
public EFAssociationProvider(EFColumnProvider column, NavigationProperty navigationProperty) {
|
||||
FromColumn = column;
|
||||
|
||||
var entityMemberParentEntity = (EFTableProvider)column.Table;
|
||||
var parentEntityModel = (EFDataModelProvider)entityMemberParentEntity.DataModel;
|
||||
|
||||
EFColumnProvider columnProvider;
|
||||
EntityType otherEntityType = navigationProperty.ToEndMember.GetEntityType();
|
||||
|
||||
// If we can get to the entityType of the ToMember side of the relaionship then build a relationship key and try to lookup the column provider.
|
||||
if (otherEntityType != null) {
|
||||
long key = BuildRelationshipKey(otherEntityType, navigationProperty.ToEndMember);
|
||||
if (parentEntityModel.RelationshipEndLookup.TryGetValue(key, out columnProvider)) {
|
||||
ToColumn = columnProvider;
|
||||
}
|
||||
else {
|
||||
// Otherwise just lookup the entityType in the table lookup
|
||||
ToTable = parentEntityModel.TableEndLookup[otherEntityType];
|
||||
}
|
||||
}
|
||||
else {
|
||||
EntityType value = (EntityType)navigationProperty.ToEndMember.TypeUsage.EdmType.MetadataProperties.Single(prop => prop.Name == "ElementType").Value;
|
||||
ToTable = parentEntityModel.TableEndLookup[value];
|
||||
}
|
||||
|
||||
if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
|
||||
if (navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
|
||||
Direction = AssociationDirection.ManyToMany;
|
||||
}
|
||||
else {
|
||||
Direction = AssociationDirection.OneToMany;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
|
||||
Direction = AssociationDirection.ManyToOne;
|
||||
}
|
||||
else {
|
||||
Direction = AssociationDirection.OneToOne;
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a foreign key reference (as opposed to a entity set), figure out the foreign keys
|
||||
if (IsForeignKeyReference) {
|
||||
var foreignKeyNames = new List<string>();
|
||||
var primaryKeyNames = FromColumn.Table.Columns.Where(c => c.IsPrimaryKey).Select(c => c.Name);
|
||||
|
||||
// Add the foreign keys for this association.
|
||||
foreignKeyNames.AddRange(GetDependentPropertyNames(navigationProperty));
|
||||
|
||||
if (IsZeroOrOne(navigationProperty)) {
|
||||
// Assume this is true for 1 to 0..1 relationships on both sides
|
||||
IsPrimaryKeyInThisTable = true;
|
||||
}
|
||||
else {
|
||||
// If any of the foreign keys are also PKs, set the flag
|
||||
IsPrimaryKeyInThisTable = foreignKeyNames.Any(fkName => primaryKeyNames.Contains(fkName, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (!foreignKeyNames.Any()) {
|
||||
// If we couldn't find any dependent properties, we're dealing with a model that doesn't
|
||||
// have FKs, and requires the use of flattened FK names (e.g. Category.CategoryId)
|
||||
foreach (ColumnProvider toEntityColumn in ToTable.Columns.Where(c => c.IsPrimaryKey)) {
|
||||
foreignKeyNames.Add(FromColumn.Name + "." + toEntityColumn.Name);
|
||||
}
|
||||
}
|
||||
|
||||
ForeignKeyNames = foreignKeyNames.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsZeroOrOne(NavigationProperty navigationProperty) {
|
||||
return (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne &&
|
||||
navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) ||
|
||||
(navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One &&
|
||||
navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);
|
||||
}
|
||||
|
||||
private bool IsForeignKeyReference {
|
||||
get {
|
||||
return Direction == AssociationDirection.OneToOne || Direction == AssociationDirection.ManyToOne;
|
||||
}
|
||||
}
|
||||
|
||||
internal static long BuildRelationshipKey(EntityType entityType, RelationshipEndMember member) {
|
||||
return Misc.CombineHashCodes(entityType.GetHashCode(), member.GetHashCode());
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> GetDependentPropertyNames(NavigationProperty navigationProperty) {
|
||||
return GetDependentPropertyNames(navigationProperty, true /*checkRelationshipType*/);
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> GetDependentPropertyNames(NavigationProperty navigationProperty, bool checkRelationshipType) {
|
||||
if (checkRelationshipType) {
|
||||
if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne &&
|
||||
navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) {
|
||||
// Get constraint when this association is on the "parent" (aka "from") side. This means
|
||||
// that the navProperty represents a Children association in a 1-1 relationship. For example,
|
||||
// this could be a Item-ItemDetail scenario where the ItemDetail table has a PK that is also an FK
|
||||
// into the Item table (thus ensuring the 1-1 cardinality). We need to special case this situation because normally we would try
|
||||
// to build a foreign key name of the form "Item.ItemID", but we want just "ItemID".
|
||||
AssociationType relationshipType = (AssociationType)navigationProperty.RelationshipType;
|
||||
ReferentialConstraint constraint = relationshipType.ReferentialConstraints.FirstOrDefault(c => c.ToRole == navigationProperty.ToEndMember);
|
||||
if (constraint != null) {
|
||||
return constraint.FromProperties.Select(p => p.Name);
|
||||
}
|
||||
|
||||
// Fall back on the primary keys if no constraints were found but only if we are on the parent side. i.e the 1 side Item side in an Item-ItemDetail
|
||||
// Get the primary keys on the "from" side of the relationship. i.e Product.Category -> ProductID
|
||||
return navigationProperty.FromEndMember.GetEntityType().KeyMembers.Select(m => m.Name);
|
||||
}
|
||||
}
|
||||
return navigationProperty.GetDependentProperties().Select(m => m.Name);
|
||||
}
|
||||
|
||||
public override string GetSortExpression(ColumnProvider sortColumn) {
|
||||
return GetSortExpression(sortColumn, "{0}.{1}");
|
||||
}
|
||||
}
|
||||
}
|
150
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFColumnProvider.cs
vendored
Normal file
150
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFColumnProvider.cs
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class EFColumnProvider : ColumnProvider {
|
||||
private EFTableProvider _table;
|
||||
private EFAssociationProvider _association;
|
||||
private bool _isAssociation;
|
||||
private bool _isSortableProcessed;
|
||||
private const string StoreGeneratedMetadata = "http://schemas.microsoft.com/ado/2009/02/edm/annotation:StoreGeneratedPattern";
|
||||
|
||||
public EFColumnProvider(EntityType entityType, EFTableProvider table, EdmMember m, bool isPrimaryKey)
|
||||
: base(table) {
|
||||
EdmMember = m;
|
||||
IsPrimaryKey = isPrimaryKey;
|
||||
_table = table;
|
||||
MaxLength = 0;
|
||||
Name = EdmMember.Name;
|
||||
//
|
||||
IsCustomProperty = false;
|
||||
|
||||
//
|
||||
var property = EdmMember as EdmProperty;
|
||||
|
||||
if (property != null) {
|
||||
IsForeignKeyComponent = DetermineIsForeignKeyComponent(property);
|
||||
IsGenerated = IsServerGenerated(property);
|
||||
}
|
||||
|
||||
ProcessFacets();
|
||||
|
||||
var navProp = m as NavigationProperty;
|
||||
if (navProp != null) {
|
||||
_isAssociation = true;
|
||||
long key = EFAssociationProvider.BuildRelationshipKey(entityType, navProp.FromEndMember);
|
||||
((EFDataModelProvider)table.DataModel).RelationshipEndLookup[key] = this;
|
||||
}
|
||||
}
|
||||
|
||||
private bool DetermineIsForeignKeyComponent(EdmProperty property) {
|
||||
var navigationProperties = property.DeclaringType.Members.OfType<NavigationProperty>();
|
||||
|
||||
// Look at all NavigationProperties (i.e. strongly-type relationship columns) of the table this column belong to and
|
||||
// see if there is a foreign key that matches this property
|
||||
// If this is a 1 to 0..1 relationship and we are processing the more primary side. i.e in the Student in Student-StudentDetail
|
||||
// and this is the primary key we don't want to check the relationship type since if there are no constraints we will treat the primary key as a foreign key.
|
||||
return navigationProperties.Any(n => EFAssociationProvider.GetDependentPropertyNames(n, !IsPrimaryKey /* checkRelationshipType */).Contains(property.Name));
|
||||
}
|
||||
|
||||
private static bool IsServerGenerated(EdmProperty property) {
|
||||
MetadataProperty generated;
|
||||
if (property.MetadataProperties.TryGetValue(StoreGeneratedMetadata, false, out generated)) {
|
||||
return "Identity" == (string)generated.Value || "Computed" == (string)generated.Value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ProcessFacets() {
|
||||
foreach (Facet facet in EdmMember.TypeUsage.Facets) {
|
||||
switch (facet.Name) {
|
||||
case "MaxLength":
|
||||
if (facet.IsUnbounded) {
|
||||
// If it's marked as unbounded, treat it as max int
|
||||
MaxLength = Int32.MaxValue;
|
||||
}
|
||||
else if (facet.Value != null && facet.Value is int) {
|
||||
MaxLength = (int)facet.Value;
|
||||
}
|
||||
break;
|
||||
case "Nullable":
|
||||
Nullable = (bool)facet.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal EdmMember EdmMember {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#region IEntityMember Members
|
||||
|
||||
public override PropertyInfo EntityTypeProperty {
|
||||
//
|
||||
get { return _table.EntityType.GetProperty(Name); }
|
||||
}
|
||||
|
||||
public override Type ColumnType {
|
||||
get {
|
||||
if (base.ColumnType == null) {
|
||||
//
|
||||
|
||||
|
||||
var edmType = EdmMember.TypeUsage.EdmType;
|
||||
if (edmType is EntityType) {
|
||||
base.ColumnType = ((EFDataModelProvider)this.Table.DataModel).GetClrType(edmType);
|
||||
}
|
||||
else if (edmType is CollectionType) {
|
||||
// get the EdmType that this CollectionType is wrapping
|
||||
base.ColumnType = ((EFDataModelProvider)this.Table.DataModel).GetClrType(((CollectionType)edmType).TypeUsage.EdmType);
|
||||
}
|
||||
else if (edmType is PrimitiveType) {
|
||||
base.ColumnType = ((PrimitiveType)edmType).ClrEquivalentType;
|
||||
}
|
||||
else if (edmType is EnumType) {
|
||||
base.ColumnType = ((EFDataModelProvider)this.Table.DataModel).GetClrType((EnumType)edmType);
|
||||
}
|
||||
else {
|
||||
Debug.Assert(false, String.Format(CultureInfo.CurrentCulture, "Unknown EdmType {0}.", edmType.GetType().FullName));
|
||||
}
|
||||
}
|
||||
return base.ColumnType;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsSortable {
|
||||
get {
|
||||
if (!_isSortableProcessed) {
|
||||
base.IsSortable = (ColumnType != typeof(byte[]));
|
||||
_isSortableProcessed = true;
|
||||
}
|
||||
return base.IsSortable;
|
||||
}
|
||||
}
|
||||
|
||||
public override AssociationProvider Association {
|
||||
get {
|
||||
if (!_isAssociation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_association == null) {
|
||||
_association = new EFAssociationProvider(this, (NavigationProperty)EdmMember);
|
||||
}
|
||||
return _association;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal static bool IsSupportedEdmMemberType(EdmMember member) {
|
||||
var edmType = member.TypeUsage.EdmType;
|
||||
return edmType is EntityType || edmType is CollectionType || edmType is PrimitiveType || edmType is EnumType;
|
||||
}
|
||||
}
|
||||
}
|
129
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFDataModelProvider.cs
vendored
Normal file
129
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFDataModelProvider.cs
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Objects;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class EFDataModelProvider : DataModelProvider {
|
||||
private ReadOnlyCollection<TableProvider> _tables;
|
||||
|
||||
internal Dictionary<long, EFColumnProvider> RelationshipEndLookup { get; private set; }
|
||||
internal Dictionary<EntityType, EFTableProvider> TableEndLookup { get; private set; }
|
||||
private Func<object> ContextFactory { get; set; }
|
||||
private Dictionary<EdmType, Type> _entityTypeToClrType = new Dictionary<EdmType, Type>();
|
||||
private ObjectContext _context;
|
||||
private ObjectItemCollection _objectSpaceItems;
|
||||
|
||||
public EFDataModelProvider(object contextInstance, Func<object> contextFactory) {
|
||||
ContextFactory = contextFactory;
|
||||
RelationshipEndLookup = new Dictionary<long, EFColumnProvider>();
|
||||
TableEndLookup = new Dictionary<EntityType, EFTableProvider>();
|
||||
|
||||
_context = (ObjectContext)contextInstance ?? (ObjectContext)CreateContext();
|
||||
ContextType = _context.GetType();
|
||||
|
||||
// get a "container" (a scope at the instance level)
|
||||
EntityContainer container = _context.MetadataWorkspace.GetEntityContainer(_context.DefaultContainerName, DataSpace.CSpace);
|
||||
// load object space metadata
|
||||
_context.MetadataWorkspace.LoadFromAssembly(ContextType.Assembly);
|
||||
_objectSpaceItems = (ObjectItemCollection)_context.MetadataWorkspace.GetItemCollection(DataSpace.OSpace);
|
||||
|
||||
var tables = new List<TableProvider>();
|
||||
|
||||
// Create a dictionary from entity type to entity set. The entity type should be at the root of any inheritance chain.
|
||||
IDictionary<EntityType, EntitySet> entitySetLookup = container.BaseEntitySets.OfType<EntitySet>().ToDictionary(e => e.ElementType);
|
||||
|
||||
// Create a lookup from parent entity to entity
|
||||
ILookup<EntityType, EntityType> derivedTypesLookup = _context.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).ToLookup(e => (EntityType)e.BaseType);
|
||||
|
||||
// Keeps track of the current entity set being processed
|
||||
EntitySet currentEntitySet = null;
|
||||
|
||||
// Do a DFS to get the inheritance hierarchy in order
|
||||
// i.e. Consider the hierarchy
|
||||
// null -> Person
|
||||
// Person -> Employee, Contact
|
||||
// Employee -> SalesPerson, Programmer
|
||||
// We'll walk the children in a depth first order -> Person, Employee, SalesPerson, Programmer, Contact.
|
||||
var objectStack = new Stack<EntityType>();
|
||||
// Start will null (the root of the hierarchy)
|
||||
objectStack.Push(null);
|
||||
while (objectStack.Any()) {
|
||||
EntityType entityType = objectStack.Pop();
|
||||
if (entityType != null) {
|
||||
// Update the entity set when we are at another root type (a type without a base type).
|
||||
if (entityType.BaseType == null) {
|
||||
currentEntitySet = entitySetLookup[entityType];
|
||||
}
|
||||
|
||||
var table = CreateTableProvider(currentEntitySet, entityType);
|
||||
tables.Add(table);
|
||||
}
|
||||
|
||||
foreach (EntityType derivedEntityType in derivedTypesLookup[entityType]) {
|
||||
// Push the derived entity types on the stack
|
||||
objectStack.Push(derivedEntityType);
|
||||
}
|
||||
}
|
||||
|
||||
_tables = tables.AsReadOnly();
|
||||
}
|
||||
|
||||
public override object CreateContext() {
|
||||
return ContextFactory();
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<TableProvider> Tables {
|
||||
get {
|
||||
return _tables;
|
||||
}
|
||||
}
|
||||
|
||||
internal Type GetClrType(EdmType entityType) {
|
||||
var result = _entityTypeToClrType[entityType];
|
||||
Debug.Assert(result != null, String.Format(CultureInfo.CurrentCulture, "Cannot map EdmType '{0}' to matching CLR Type", entityType));
|
||||
return result;
|
||||
}
|
||||
|
||||
internal Type GetClrType(EnumType enumType) {
|
||||
var objectSpaceType = (EnumType)_context.MetadataWorkspace.GetObjectSpaceType(enumType);
|
||||
return _objectSpaceItems.GetClrType(objectSpaceType);
|
||||
}
|
||||
|
||||
private Type GetClrType(EntityType entityType) {
|
||||
var objectSpaceType = (EntityType)_context.MetadataWorkspace.GetObjectSpaceType(entityType);
|
||||
return _objectSpaceItems.GetClrType(objectSpaceType);
|
||||
}
|
||||
|
||||
private TableProvider CreateTableProvider(EntitySet entitySet, EntityType entityType) {
|
||||
// Get the parent clr type
|
||||
Type parentClrType = null;
|
||||
EntityType parentEntityType = entityType.BaseType as EntityType;
|
||||
if (parentEntityType != null) {
|
||||
parentClrType = GetClrType(parentEntityType);
|
||||
}
|
||||
|
||||
Type rootClrType = GetClrType(entitySet.ElementType);
|
||||
Type clrType = GetClrType(entityType);
|
||||
|
||||
_entityTypeToClrType[entityType] = clrType;
|
||||
|
||||
// Normally, use the entity set name as the table name
|
||||
string tableName = entitySet.Name;
|
||||
|
||||
// But in inheritance scenarios where all types in the hierarchy share the same entity set,
|
||||
// we need to use the type name instead for the table name.
|
||||
if (parentClrType != null) {
|
||||
tableName = entityType.Name;
|
||||
}
|
||||
|
||||
EFTableProvider table = new EFTableProvider(this, entitySet, entityType, clrType, parentClrType, rootClrType, tableName);
|
||||
TableEndLookup[entityType] = table;
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
73
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFTableProvider.cs
vendored
Normal file
73
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/EFTableProvider.cs
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Objects;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal sealed class EFTableProvider : TableProvider {
|
||||
private ReadOnlyCollection<ColumnProvider> _roColumns;
|
||||
|
||||
public EFTableProvider(EFDataModelProvider dataModel, EntitySet entitySet, EntityType entityType,
|
||||
Type entityClrType, Type parentEntityClrType, Type rootEntityClrType, string name)
|
||||
: base(dataModel) {
|
||||
|
||||
EntityType = entityClrType;
|
||||
Name = name;
|
||||
DataContextPropertyName = entitySet.Name;
|
||||
ParentEntityType = parentEntityClrType;
|
||||
RootEntityType = rootEntityClrType;
|
||||
|
||||
var genericMethod = DataModel.ContextType.GetMethod("CreateQuery");
|
||||
CreateQueryMethod = genericMethod.MakeGenericMethod(EntityType);
|
||||
CreateQueryString = CreateEntitySqlQueryString(entitySet);
|
||||
|
||||
var keyMembers = entityType.KeyMembers;
|
||||
|
||||
// columns (entity properties)
|
||||
// note 1: keys are also available through es.ElementType.KeyMembers
|
||||
// note 2: this includes "nav properties", kind of fancy, two-way relationship objects
|
||||
var columns = new List<ColumnProvider>();
|
||||
foreach (EdmMember m in entityType.Members) {
|
||||
if (EFColumnProvider.IsSupportedEdmMemberType(m) && IsPublicProperty(entityClrType, m.Name)) {
|
||||
EFColumnProvider entityMember = new EFColumnProvider(entityType, this, m, keyMembers.Contains(m));
|
||||
columns.Add(entityMember);
|
||||
}
|
||||
}
|
||||
|
||||
_roColumns = new ReadOnlyCollection<ColumnProvider>(columns);
|
||||
}
|
||||
|
||||
private static bool IsPublicProperty(Type entityClrType, string propertyName) {
|
||||
var property = entityClrType.GetProperty(propertyName);
|
||||
return property != null && property.GetGetMethod() != null;
|
||||
}
|
||||
|
||||
private MethodInfo CreateQueryMethod { get; set; }
|
||||
|
||||
private string CreateQueryString { get; set; }
|
||||
|
||||
private static string CreateEntitySqlQueryString(EntitySet entitySet) {
|
||||
// Qualify the entity set name with the container name (in case the ObjectContext's default
|
||||
// container name is not set or has an unexpected value)
|
||||
return QuoteEntitySqlIdentifier(entitySet.EntityContainer.Name) + "." + QuoteEntitySqlIdentifier(entitySet.Name);
|
||||
}
|
||||
|
||||
private static string QuoteEntitySqlIdentifier(string identifier) {
|
||||
// Enclose in square brackets and escape the one reserved character (])
|
||||
return "[" + identifier.Replace("]", "]]") + "]";
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<ColumnProvider> Columns {
|
||||
get {
|
||||
return _roColumns;
|
||||
}
|
||||
}
|
||||
|
||||
public override IQueryable GetQuery(object context) {
|
||||
return (IQueryable)CreateQueryMethod.Invoke(context,
|
||||
new object[] { CreateQueryString, new ObjectParameter[0] });
|
||||
}
|
||||
}
|
||||
}
|
44
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/SchemaCreator.cs
vendored
Normal file
44
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/SchemaCreator.cs
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Data.Linq;
|
||||
using System.Data.Objects;
|
||||
using System.Globalization;
|
||||
using System.Web.Resources;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
internal class SchemaCreator {
|
||||
private static SchemaCreator s_instance = new SchemaCreator();
|
||||
|
||||
public static SchemaCreator Instance {
|
||||
get {
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual DataModelProvider CreateDataModel(object contextInstance, Func<object> contextFactory) {
|
||||
if (IsDataContext(contextInstance.GetType())) {
|
||||
return new DLinqDataModelProvider(contextInstance, contextFactory);
|
||||
}
|
||||
if (IsObjectContext(contextInstance.GetType())) {
|
||||
return new EFDataModelProvider(contextInstance, contextFactory);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.SchemaCreator_UnknownModel, contextInstance.GetType().FullName));
|
||||
}
|
||||
|
||||
public virtual bool ValidDataContextType(Type contextType) {
|
||||
//
|
||||
return IsDataContext(contextType) || IsObjectContext(contextType);
|
||||
}
|
||||
|
||||
internal static bool IsDataContext(Type contextType) {
|
||||
return IsValidType<DataContext>(contextType);
|
||||
}
|
||||
|
||||
internal static bool IsObjectContext(Type contextType) {
|
||||
return IsValidType<ObjectContext>(contextType);
|
||||
}
|
||||
|
||||
private static bool IsValidType<T>(Type contextType) where T : class {
|
||||
return contextType != null && typeof(T).IsAssignableFrom(contextType);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Resources;
|
||||
|
||||
internal sealed class SimpleColumnProvider : ColumnProvider {
|
||||
public SimpleColumnProvider(TableProvider tableProvider, PropertyDescriptor propertyDescriptor)
|
||||
: base(tableProvider) {
|
||||
if (propertyDescriptor.PropertyType == null) {
|
||||
throw new ArgumentNullException(DynamicDataResources.SimpleColumnProvider_ColumnTypeRequired);
|
||||
}
|
||||
Name = propertyDescriptor.Name;
|
||||
ColumnType = propertyDescriptor.PropertyType;
|
||||
IsPrimaryKey = propertyDescriptor.Attributes.OfType<KeyAttribute>().Any();
|
||||
Nullable = Misc.TypeAllowsNull(ColumnType);
|
||||
IsReadOnly = propertyDescriptor.IsReadOnly;
|
||||
IsSortable = true;
|
||||
}
|
||||
|
||||
public override AttributeCollection Attributes {
|
||||
get {
|
||||
if (!System.Web.UI.DataBinder.IsBindableType(ColumnType)) {
|
||||
return AttributeCollection.FromExisting(base.Attributes, new ScaffoldColumnAttribute(false));
|
||||
}
|
||||
return base.Attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
|
||||
internal class SimpleDataModelProvider : DataModelProvider {
|
||||
private List<TableProvider> _tables = new List<TableProvider>();
|
||||
|
||||
public SimpleDataModelProvider(Type entityType) {
|
||||
_tables.Add(new SimpleTableProvider(this, entityType));
|
||||
}
|
||||
|
||||
public SimpleDataModelProvider(ICustomTypeDescriptor typeDescriptor) {
|
||||
_tables.Add(new SimpleTableProvider(this, typeDescriptor));
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<TableProvider> Tables {
|
||||
get {
|
||||
return _tables.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public override object CreateContext() {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
|
||||
internal sealed class SimpleTableProvider : TableProvider {
|
||||
private List<ColumnProvider> _columns;
|
||||
private ICustomTypeDescriptor _descriptor;
|
||||
|
||||
public SimpleTableProvider(DataModelProvider modelProvider, Type entityType)
|
||||
: base(modelProvider) {
|
||||
|
||||
if (entityType == null) {
|
||||
throw new ArgumentNullException("entityType");
|
||||
}
|
||||
|
||||
EntityType = entityType;
|
||||
Name = entityType.Name;
|
||||
DataContextPropertyName = String.Empty;
|
||||
InitializeColumns(TypeDescriptor.GetProperties(entityType));
|
||||
}
|
||||
|
||||
public SimpleTableProvider(DataModelProvider modelProvider, ICustomTypeDescriptor descriptor)
|
||||
: base(modelProvider) {
|
||||
|
||||
if (descriptor == null) {
|
||||
throw new ArgumentNullException("descriptor");
|
||||
}
|
||||
|
||||
_descriptor = descriptor;
|
||||
Name = descriptor.GetClassName();
|
||||
DataContextPropertyName = String.Empty;
|
||||
InitializeColumns(descriptor.GetProperties());
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<ColumnProvider> Columns {
|
||||
get {
|
||||
return _columns.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public override ICustomTypeDescriptor GetTypeDescriptor() {
|
||||
return _descriptor ?? base.GetTypeDescriptor();
|
||||
}
|
||||
|
||||
public override IQueryable GetQuery(object context) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private void InitializeColumns(PropertyDescriptorCollection columnDescriptors) {
|
||||
_columns = columnDescriptors.OfType<PropertyDescriptor>().Select(p => new SimpleColumnProvider(this, p)).OfType<ColumnProvider>().ToList();
|
||||
}
|
||||
}
|
||||
}
|
159
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/TableProvider.cs
vendored
Normal file
159
external/referencesource/System.Web.DynamicData/DynamicData/ModelProviders/TableProvider.cs
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace System.Web.DynamicData.ModelProviders {
|
||||
/// <summary>
|
||||
/// Base provider class for tables.
|
||||
/// Each provider type (e.g. Linq To Sql, Entity Framework, 3rd party) extends this class.
|
||||
/// </summary>
|
||||
public abstract class TableProvider {
|
||||
private Type _rootEntityType;
|
||||
private string _dataContextPropertyName;
|
||||
|
||||
internal TableProvider() {
|
||||
// for unit testing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="model">the model this table belongs to</param>
|
||||
protected TableProvider(DataModelProvider model) {
|
||||
if (model == null) {
|
||||
throw new ArgumentNullException("model");
|
||||
}
|
||||
|
||||
DataModel = model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// readable representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public override string ToString() {
|
||||
// To help identifying objects in debugger
|
||||
return Name ?? base.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to attributes defined for the table represented by this provider.
|
||||
/// </summary>
|
||||
public virtual AttributeCollection Attributes {
|
||||
get {
|
||||
return GetTypeDescriptor().GetAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ICustomTypeDescriptor GetTypeDescriptor() {
|
||||
return TypeDescriptor.GetProvider(EntityType).GetTypeDescriptor(EntityType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the table. Typically, this is the name of the property in the data context class
|
||||
/// </summary>
|
||||
public virtual string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The CLR type that represents this table
|
||||
/// </summary>
|
||||
public virtual Type EntityType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The collection of columns in this table
|
||||
/// </summary>
|
||||
public abstract ReadOnlyCollection<ColumnProvider> Columns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The IQueryable that returns the elements of this table
|
||||
/// </summary>
|
||||
public abstract IQueryable GetQuery(object context);
|
||||
|
||||
/// <summary>
|
||||
/// The data model provider that this table is part of
|
||||
/// </summary>
|
||||
public DataModelProvider DataModel { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a foreign key for a given row. By default, it just looks up a property by that name
|
||||
/// </summary>
|
||||
public virtual object EvaluateForeignKey(object row, string foreignKeyName) {
|
||||
return System.Web.UI.DataBinder.GetPropertyValue(row, foreignKeyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the parent 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 null.
|
||||
/// </summary>
|
||||
public virtual Type ParentEntityType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual Type RootEntityType {
|
||||
get {
|
||||
return _rootEntityType ?? EntityType;
|
||||
}
|
||||
protected set {
|
||||
_rootEntityType = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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. If this value has not
|
||||
/// been set, it will return the value of the Name property.
|
||||
/// </summary>
|
||||
public virtual string DataContextPropertyName {
|
||||
get {
|
||||
return _dataContextPropertyName ?? Name;
|
||||
}
|
||||
protected set {
|
||||
_dataContextPropertyName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in user is allowed to delete items from the table
|
||||
/// </summary>
|
||||
public virtual bool CanDelete(IPrincipal principal) {
|
||||
if (principal == null) {
|
||||
throw new ArgumentNullException("principal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in user is allowed to insert into the table
|
||||
/// </summary>
|
||||
public virtual bool CanInsert(IPrincipal principal) {
|
||||
if (principal == null) {
|
||||
throw new ArgumentNullException("principal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in user is allowed to read from the table
|
||||
/// </summary>
|
||||
public virtual bool CanRead(IPrincipal principal) {
|
||||
if (principal == null) {
|
||||
throw new ArgumentNullException("principal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in user is allowed to make changes tothe table
|
||||
/// </summary>
|
||||
public virtual bool CanUpdate(IPrincipal principal) {
|
||||
if (principal == null) {
|
||||
throw new ArgumentNullException("principal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user