e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1830 lines
76 KiB
C#
1830 lines
76 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Linq;
|
|
using System.Data.Linq.Provider;
|
|
using System.Data.Linq.Mapping;
|
|
using System.Data.Linq.SqlClient;
|
|
using System.Threading;
|
|
using System.Runtime.Versioning;
|
|
using LinqToSqlShared.Mapping;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace System.Data.Linq.Mapping {
|
|
|
|
internal class MappedMetaModel : MetaModel {
|
|
ReaderWriterLock @lock = new ReaderWriterLock();
|
|
MappingSource mappingSource;
|
|
Type contextType;
|
|
Type providerType;
|
|
DatabaseMapping mapping;
|
|
HashSet<Module> modules;
|
|
Dictionary<string, Type> types;
|
|
Dictionary<Type, MetaType> metaTypes;
|
|
Dictionary<Type, MetaTable> metaTables;
|
|
Dictionary<MetaPosition, MetaFunction> metaFunctions;
|
|
bool fullyLoaded;
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // mapping parameter contains various type references.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
internal MappedMetaModel(MappingSource mappingSource, Type contextType, DatabaseMapping mapping) {
|
|
this.mappingSource = mappingSource;
|
|
this.contextType = contextType;
|
|
this.mapping = mapping;
|
|
this.modules = new HashSet<Module>();
|
|
this.modules.Add(this.contextType.Module);
|
|
this.metaTypes = new Dictionary<Type, MetaType>();
|
|
this.metaTables = new Dictionary<Type, MetaTable>();
|
|
this.types = new Dictionary<string, Type>();
|
|
// Provider type
|
|
if (this.providerType == null && !String.IsNullOrEmpty(this.mapping.Provider)) {
|
|
this.providerType = this.FindType(this.mapping.Provider, typeof(SqlProvider).Namespace);
|
|
if (this.providerType == null) {
|
|
throw Error.ProviderTypeNotFound(this.mapping.Provider);
|
|
}
|
|
}
|
|
else if (this.providerType == null) {
|
|
this.providerType = typeof(SqlProvider);
|
|
}
|
|
this.Init();
|
|
}
|
|
#region Initialization
|
|
private void Init() {
|
|
if (!fullyLoaded) {
|
|
// The fullyLoaded state is required so that tools like
|
|
// CreateDatabase can get a full view of all tables.
|
|
@lock.AcquireWriterLock(Timeout.Infinite);
|
|
try {
|
|
if (!fullyLoaded) {
|
|
// Initialize static tables and functions.
|
|
this.InitStaticTables();
|
|
this.InitFunctions();
|
|
fullyLoaded = true;
|
|
}
|
|
}
|
|
finally {
|
|
@lock.ReleaseWriterLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)] // Exposure is via external mapping file/attributes.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
private void InitStaticTables() {
|
|
this.InitStaticTableTypes();
|
|
foreach (TableMapping tableMapping in this.mapping.Tables) {
|
|
Type rowType = this.FindType(tableMapping.RowType.Name);
|
|
if (rowType == null) {
|
|
throw Error.CouldNotFindTypeFromMapping(tableMapping.RowType.Name);
|
|
}
|
|
Type rootType = this.GetRootType(rowType, tableMapping.RowType);
|
|
MetaTable table = new MappedTable(this, tableMapping, rootType);
|
|
foreach (MetaType mt in table.RowType.InheritanceTypes) {
|
|
this.metaTypes.Add(mt.Type, mt);
|
|
this.metaTables.Add(mt.Type, table);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitStaticTableTypes() {
|
|
for (Type type = this.contextType; type != typeof(DataContext); type = type.BaseType) {
|
|
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
foreach (FieldInfo fi in fields) {
|
|
Type ft = fi.FieldType;
|
|
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(Table<>)) {
|
|
Type rowType = ft.GetGenericArguments()[0];
|
|
if (!this.types.ContainsKey(rowType.Name)) {
|
|
this.types.Add(rowType.FullName, rowType);
|
|
if (!this.types.ContainsKey(rowType.Name)) {
|
|
this.types.Add(rowType.Name, rowType);
|
|
}
|
|
this.modules.Add(rowType.Module);
|
|
}
|
|
}
|
|
}
|
|
PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
foreach (PropertyInfo pi in props) {
|
|
Type pt = pi.PropertyType;
|
|
if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Table<>)) {
|
|
Type rowType = pt.GetGenericArguments()[0];
|
|
if (!this.types.ContainsKey(rowType.Name)) {
|
|
this.types.Add(rowType.FullName, rowType);
|
|
if (!this.types.ContainsKey(rowType.Name)) {
|
|
this.types.Add(rowType.Name, rowType);
|
|
}
|
|
this.modules.Add(rowType.Module);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)] // mapping instance variable is set elsewhere.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // For GetMethods call.
|
|
private void InitFunctions() {
|
|
this.metaFunctions = new Dictionary<MetaPosition, MetaFunction>();
|
|
if (this.contextType != typeof(DataContext)) {
|
|
foreach (FunctionMapping fmap in this.mapping.Functions) {
|
|
MethodInfo method = this.GetMethod(fmap.MethodName);
|
|
if (method == null) {
|
|
throw Error.MethodCannotBeFound(fmap.MethodName);
|
|
}
|
|
MappedFunction func = new MappedFunction(this, fmap, method);
|
|
this.metaFunctions.Add(new MetaPosition(method), func);
|
|
|
|
// pre-set all known function result types into metaType map
|
|
foreach (MetaType rt in func.ResultRowTypes) {
|
|
foreach (MetaType it in rt.InheritanceTypes) {
|
|
if (!this.metaTypes.ContainsKey(it.Type)) {
|
|
this.metaTypes.Add(it.Type, it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public override MappingSource MappingSource {
|
|
get { return this.mappingSource; }
|
|
}
|
|
|
|
public override Type ContextType {
|
|
get { return this.contextType; }
|
|
}
|
|
|
|
public override string DatabaseName {
|
|
get { return this.mapping.DatabaseName; }
|
|
}
|
|
|
|
public override Type ProviderType {
|
|
get { return this.providerType; }
|
|
}
|
|
|
|
public override IEnumerable<MetaTable> GetTables() {
|
|
return this.metaTables.Values.Where(x => x != null).Distinct();
|
|
}
|
|
|
|
public override MetaTable GetTable(Type rowType) {
|
|
if (rowType == null) {
|
|
throw Error.ArgumentNull("rowType");
|
|
}
|
|
MetaTable table = null;
|
|
this.metaTables.TryGetValue(rowType, out table);
|
|
return table;
|
|
}
|
|
|
|
public override MetaType GetMetaType(Type type) {
|
|
if (type == null) {
|
|
throw Error.ArgumentNull("type");
|
|
}
|
|
MetaType mtype = null;
|
|
@lock.AcquireReaderLock(Timeout.Infinite);
|
|
try {
|
|
if (this.metaTypes.TryGetValue(type, out mtype)) {
|
|
return mtype;
|
|
}
|
|
}
|
|
finally {
|
|
@lock.ReleaseReaderLock();
|
|
}
|
|
@lock.AcquireWriterLock(Timeout.Infinite);
|
|
try {
|
|
if (!this.metaTypes.TryGetValue(type, out mtype)) {
|
|
// not known, so must be unmapped type
|
|
mtype = new UnmappedType(this, type);
|
|
this.metaTypes.Add(type, mtype);
|
|
}
|
|
}
|
|
finally {
|
|
@lock.ReleaseWriterLock();
|
|
}
|
|
return mtype;
|
|
}
|
|
|
|
public override MetaFunction GetFunction(MethodInfo method) {
|
|
if (method == null) {
|
|
throw new ArgumentNullException("method");
|
|
}
|
|
MetaFunction func = null;
|
|
this.metaFunctions.TryGetValue(new MetaPosition(method), out func);
|
|
return func;
|
|
}
|
|
|
|
public override IEnumerable<MetaFunction> GetFunctions() {
|
|
return this.metaFunctions.Values;
|
|
}
|
|
|
|
private Type GetRootType(Type type, TypeMapping rootMapping) {
|
|
if (string.Compare(rootMapping.Name, type.Name, StringComparison.Ordinal) == 0
|
|
|| string.Compare(rootMapping.Name, type.FullName, StringComparison.Ordinal) == 0
|
|
|| string.Compare(rootMapping.Name, type.AssemblyQualifiedName, StringComparison.Ordinal) == 0)
|
|
return type;
|
|
if (type.BaseType != typeof(object)) {
|
|
return this.GetRootType(type.BaseType, rootMapping);
|
|
}
|
|
throw Error.UnableToResolveRootForType(type);
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter will be found on a type.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
private MethodInfo GetMethod(string name) {
|
|
string typeName, methodName;
|
|
this.GetTypeAndMethod(name, out typeName, out methodName);
|
|
Type type = this.FindType(typeName);
|
|
if (type != null) {
|
|
return type.GetMethod(methodName, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void GetTypeAndMethod(string name, out string typeName, out string methodName) {
|
|
int dotIndex = name.LastIndexOf(".", StringComparison.CurrentCulture);
|
|
if (dotIndex > 0) {
|
|
typeName = name.Substring(0, dotIndex);
|
|
methodName = name.Substring(dotIndex + 1);
|
|
}
|
|
else {
|
|
typeName = this.contextType.FullName;
|
|
methodName = name;
|
|
}
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
internal Type FindType(string name) {
|
|
return this.FindType(name, this.contextType.Namespace);
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // SearchForType method call.
|
|
internal Type FindType(string name, string defaultNamespace) {
|
|
Type result = null;
|
|
string name2 = null;
|
|
@lock.AcquireReaderLock(Timeout.Infinite);
|
|
try {
|
|
if (this.types.TryGetValue(name, out result)) {
|
|
return result;
|
|
}
|
|
name2 = name.Contains(".") ? null : defaultNamespace + "." + name;
|
|
if (name2 != null && this.types.TryGetValue(name2, out result)) {
|
|
return result;
|
|
}
|
|
}
|
|
finally {
|
|
@lock.ReleaseReaderLock();
|
|
}
|
|
// don't block anyone while we search for the correct type
|
|
Type foundResult = this.SearchForType(name);
|
|
|
|
if (foundResult == null && name2 != null) {
|
|
foundResult = this.SearchForType(name2);
|
|
}
|
|
if (foundResult != null) {
|
|
@lock.AcquireWriterLock(Timeout.Infinite);
|
|
try {
|
|
if (this.types.TryGetValue(name, out result)) {
|
|
return result;
|
|
}
|
|
this.types.Add(name, foundResult);
|
|
return foundResult;
|
|
}
|
|
finally {
|
|
@lock.ReleaseWriterLock();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // SearchForType method call.
|
|
private Type SearchForType(string name) {
|
|
// Try search for type using case sensitive.
|
|
Type type = SearchForType(name, false);
|
|
if (type != null) {
|
|
return type;
|
|
}
|
|
|
|
// Try search for type using case in-sensitive.
|
|
return SearchForType(name, true);
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // Assembly.GetLoadedModules method call.
|
|
private Type SearchForType(string name, bool ignoreCase) {
|
|
// first try default system lookup
|
|
Type type = Type.GetType(name, false, ignoreCase);
|
|
if (type != null) {
|
|
return type;
|
|
}
|
|
|
|
// try all known modules (modules that other statically known types were found in)
|
|
foreach (Module module in this.modules) {
|
|
type = module.GetType(name, false, ignoreCase);
|
|
if (type != null) {
|
|
return type;
|
|
}
|
|
}
|
|
|
|
// try all loaded modules (is there a better way?)
|
|
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
|
|
foreach (Module module in a.GetLoadedModules()) {
|
|
type = module.GetType(name, false, ignoreCase);
|
|
if (type != null) {
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal sealed class MappedTable : MetaTable {
|
|
MappedMetaModel model;
|
|
TableMapping mapping;
|
|
MetaType rowType;
|
|
bool hasMethods;
|
|
MethodInfo insertMethod;
|
|
MethodInfo updateMethod;
|
|
MethodInfo deleteMethod;
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Parameter contains various type references.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // MappedRootType constructor call.
|
|
internal MappedTable(MappedMetaModel model, TableMapping mapping, Type rowType) {
|
|
this.model = model;
|
|
this.mapping = mapping;
|
|
this.rowType = new MappedRootType(model, this, mapping.RowType, rowType);
|
|
}
|
|
public override MetaModel Model {
|
|
get { return this.model; }
|
|
}
|
|
public override string TableName {
|
|
get { return this.mapping.TableName; }
|
|
}
|
|
public override MetaType RowType {
|
|
get { return this.rowType; }
|
|
}
|
|
public override MethodInfo InsertMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.insertMethod;
|
|
}
|
|
}
|
|
public override MethodInfo UpdateMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.updateMethod;
|
|
}
|
|
}
|
|
public override MethodInfo DeleteMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.deleteMethod;
|
|
}
|
|
}
|
|
private void InitMethods() {
|
|
if (!this.hasMethods) {
|
|
this.insertMethod = MethodFinder.FindMethod(
|
|
this.model.ContextType,
|
|
"Insert" + rowType.Name,
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
new Type[] { rowType.Type }
|
|
);
|
|
this.updateMethod = MethodFinder.FindMethod(
|
|
this.model.ContextType,
|
|
"Update" + rowType.Name,
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
new Type[] { rowType.Type }
|
|
);
|
|
this.deleteMethod = MethodFinder.FindMethod(
|
|
this.model.ContextType,
|
|
"Delete" + rowType.Name,
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
new Type[] { rowType.Type }
|
|
);
|
|
this.hasMethods = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class MappedRootType : MappedType {
|
|
Dictionary<Type, MetaType> derivedTypes;
|
|
Dictionary<object, MetaType> inheritanceCodes;
|
|
ReadOnlyCollection<MetaType> inheritanceTypes;
|
|
MetaType inheritanceDefault;
|
|
bool hasInheritance;
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Various parameters can contain type names.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // InitInheritedType method call.
|
|
public MappedRootType(MappedMetaModel model, MappedTable table, TypeMapping typeMapping, Type type)
|
|
: base(model, table, typeMapping, type, null) {
|
|
if (typeMapping == null)
|
|
throw Error.ArgumentNull("typeMapping");
|
|
|
|
if (typeMapping.InheritanceCode != null || typeMapping.DerivedTypes.Count > 0) {
|
|
if (this.Discriminator == null) {
|
|
throw Error.NoDiscriminatorFound(type.Name);
|
|
}
|
|
this.hasInheritance = true;
|
|
if (!MappingSystem.IsSupportedDiscriminatorType(this.Discriminator.Type)) {
|
|
throw Error.DiscriminatorClrTypeNotSupported(this.Discriminator.DeclaringType.Name, this.Discriminator.Name, this.Discriminator.Type);
|
|
}
|
|
this.derivedTypes = new Dictionary<Type, MetaType>();
|
|
this.inheritanceCodes = new Dictionary<object, MetaType>();
|
|
this.InitInheritedType(typeMapping, this);
|
|
}
|
|
|
|
if (this.inheritanceDefault == null && (this.inheritanceCode != null || this.inheritanceCodes != null && this.inheritanceCodes.Count > 0))
|
|
throw Error.InheritanceHierarchyDoesNotDefineDefault(type);
|
|
|
|
if (this.derivedTypes != null) {
|
|
this.inheritanceTypes = this.derivedTypes.Values.ToList().AsReadOnly();
|
|
}
|
|
else {
|
|
this.inheritanceTypes = new MetaType[] { this }.ToList().AsReadOnly();
|
|
}
|
|
|
|
this.Validate();
|
|
}
|
|
|
|
private void Validate() {
|
|
Dictionary<object, string> memberToColumn = new Dictionary<object, string>();
|
|
foreach (MetaType type in this.InheritanceTypes) {
|
|
// NB: Table node in XML can have only one Type node -- enforced by XSD
|
|
|
|
foreach (MetaDataMember mem in type.PersistentDataMembers) {
|
|
if (mem.IsDeclaredBy(type)) {
|
|
if (mem.IsDiscriminator && !this.HasInheritance) {
|
|
throw Error.NonInheritanceClassHasDiscriminator(type);
|
|
}
|
|
if (!mem.IsAssociation) {
|
|
// validate that no database column is mapped twice
|
|
if (!string.IsNullOrEmpty(mem.MappedName)) {
|
|
string column;
|
|
object dn = InheritanceRules.DistinguishedMemberName(mem.Member);
|
|
if (memberToColumn.TryGetValue(dn, out column)) {
|
|
if (column != mem.MappedName) {
|
|
throw Error.MemberMappedMoreThanOnce(mem.Member.Name);
|
|
}
|
|
}
|
|
else {
|
|
memberToColumn.Add(dn, mem.MappedName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // typeMap parameter's Name property.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
private MetaType InitDerivedTypes(TypeMapping typeMap) {
|
|
Type type = ((MappedMetaModel)Model).FindType(typeMap.Name);
|
|
if (type == null)
|
|
throw Error.CouldNotFindRuntimeTypeForMapping(typeMap.Name);
|
|
MappedType rowType = new MappedType(this.Model, this.Table, typeMap, type, this);
|
|
return this.InitInheritedType(typeMap, rowType);
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // typeMap parameter's Name property.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // InitDerivedTypes method call.
|
|
private MetaType InitInheritedType(TypeMapping typeMap, MappedType type) {
|
|
this.derivedTypes.Add(type.Type, type);
|
|
|
|
if (typeMap.InheritanceCode != null) { // Mapping with no inheritance code: For example, an unmapped intermediate class in a hierarchy.
|
|
if (this.Discriminator == null)
|
|
throw Error.NoDiscriminatorFound(type.Name);
|
|
|
|
if (type.Type.IsAbstract)
|
|
throw Error.AbstractClassAssignInheritanceDiscriminator(type.Type);
|
|
|
|
object keyValue = DBConvert.ChangeType(typeMap.InheritanceCode, this.Discriminator.Type);
|
|
foreach (object d in inheritanceCodes.Keys) {
|
|
// if the keys are equal, or if they are both strings containing only spaces
|
|
// they are considered equal
|
|
if ((keyValue.GetType() == typeof(string) && ((string)keyValue).Trim().Length == 0 &&
|
|
d.GetType() == typeof(string) && ((string)d).Trim().Length == 0) ||
|
|
object.Equals(d, keyValue)) {
|
|
throw Error.InheritanceCodeUsedForMultipleTypes(keyValue);
|
|
}
|
|
}
|
|
if (type.inheritanceCode != null)
|
|
throw Error.InheritanceTypeHasMultipleDiscriminators(type);
|
|
type.inheritanceCode = keyValue;
|
|
this.inheritanceCodes.Add(keyValue, type);
|
|
if (typeMap.IsInheritanceDefault) {
|
|
if (this.inheritanceDefault != null)
|
|
throw Error.InheritanceTypeHasMultipleDefaults(type);
|
|
this.inheritanceDefault = type;
|
|
}
|
|
}
|
|
|
|
// init sub-inherited types
|
|
foreach (TypeMapping tm in typeMap.DerivedTypes) {
|
|
this.InitDerivedTypes(tm);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
public override bool HasInheritance {
|
|
get { return this.hasInheritance; }
|
|
}
|
|
|
|
public override bool HasInheritanceCode {
|
|
get { return this.InheritanceCode != null; }
|
|
}
|
|
|
|
public override ReadOnlyCollection<MetaType> InheritanceTypes {
|
|
get { return this.inheritanceTypes; }
|
|
}
|
|
|
|
public override MetaType GetInheritanceType(Type type) {
|
|
if (type == this.Type)
|
|
return this;
|
|
MetaType metaType = null;
|
|
if (this.derivedTypes != null) {
|
|
this.derivedTypes.TryGetValue(type, out metaType);
|
|
}
|
|
return metaType;
|
|
}
|
|
|
|
public override MetaType InheritanceDefault {
|
|
get { return this.inheritanceDefault; }
|
|
}
|
|
}
|
|
|
|
internal class MappedType : MetaType {
|
|
MetaModel model;
|
|
MetaTable table;
|
|
Type type;
|
|
TypeMapping typeMapping;
|
|
Dictionary<object, MetaDataMember> dataMemberMap;
|
|
ReadOnlyCollection<MetaDataMember> dataMembers;
|
|
ReadOnlyCollection<MetaDataMember> persistentDataMembers;
|
|
ReadOnlyCollection<MetaDataMember> identities;
|
|
MetaDataMember dbGeneratedIdentity;
|
|
MetaDataMember version;
|
|
MetaDataMember discriminator;
|
|
MetaType inheritanceRoot;
|
|
bool inheritanceBaseSet;
|
|
MetaType inheritanceBase;
|
|
internal object inheritanceCode;
|
|
ReadOnlyCollection<MetaType> derivedTypes;
|
|
ReadOnlyCollection<MetaAssociation> associations;
|
|
bool hasMethods;
|
|
bool hasAnyLoadMethod;
|
|
bool hasAnyValidateMethod;
|
|
MethodInfo onLoadedMethod;
|
|
MethodInfo onValidateMethod;
|
|
|
|
object locktarget = new object(); // Hold locks on private object rather than public MetaType.
|
|
|
|
internal MappedType(MetaModel model, MetaTable table, TypeMapping typeMapping, Type type, MetaType inheritanceRoot) {
|
|
this.model = model;
|
|
this.table = table;
|
|
this.typeMapping = typeMapping;
|
|
this.type = type;
|
|
this.inheritanceRoot = inheritanceRoot != null ? inheritanceRoot : this;
|
|
this.InitDataMembers();
|
|
|
|
this.identities = this.dataMembers.Where(m => m.IsPrimaryKey).ToList().AsReadOnly();
|
|
this.persistentDataMembers = this.dataMembers.Where(m => m.IsPersistent).ToList().AsReadOnly();
|
|
}
|
|
#region Initialization
|
|
private void ValidatePrimaryKeyMember(MetaDataMember mm) {
|
|
//if the type is a sub-type, no member in the type can be primary key
|
|
if (mm.IsPrimaryKey && this.inheritanceRoot != this && mm.Member.DeclaringType == this.type) {
|
|
throw (Error.PrimaryKeyInSubTypeNotSupported(this.type.Name, mm.Name));
|
|
}
|
|
}
|
|
|
|
private void InitMethods() {
|
|
if (!this.hasMethods) {
|
|
this.onLoadedMethod = MethodFinder.FindMethod(
|
|
this.Type,
|
|
"OnLoaded",
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
Type.EmptyTypes,
|
|
false
|
|
);
|
|
this.onValidateMethod = MethodFinder.FindMethod(
|
|
this.Type,
|
|
"OnValidate",
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
new[] { typeof(ChangeAction) },
|
|
false
|
|
);
|
|
|
|
this.hasAnyLoadMethod = (this.onLoadedMethod != null) || (this.InheritanceBase != null && this.InheritanceBase.HasAnyLoadMethod);
|
|
this.hasAnyValidateMethod = (this.onValidateMethod != null) || (this.InheritanceBase != null && this.InheritanceBase.HasAnyValidateMethod);
|
|
|
|
this.hasMethods = true;
|
|
}
|
|
}
|
|
|
|
private void InitDataMembers() {
|
|
if (this.dataMembers == null) {
|
|
Dictionary<object, MetaDataMember> map = new Dictionary<object, MetaDataMember>();
|
|
List<MetaDataMember> dMembers = new List<MetaDataMember>();
|
|
int ordinal = 0;
|
|
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
|
|
|
|
// Map of valid mapped names.
|
|
Dictionary<string, MemberMapping> names = new Dictionary<string, MemberMapping>();
|
|
Type currentType = this.type;
|
|
for (TypeMapping tm = this.typeMapping; tm != null; tm = tm.BaseType) {
|
|
foreach (MemberMapping mmap in tm.Members) {
|
|
names[mmap.MemberName + ":" + currentType.Name] = mmap;
|
|
}
|
|
currentType = currentType.BaseType;
|
|
}
|
|
|
|
HashSet<string> namesSeen = new HashSet<string>(); // Keep track of which names from the mapping file have been seen.
|
|
FieldInfo[] fis = TypeSystem.GetAllFields(this.type, flags).ToArray();
|
|
if (fis != null) {
|
|
foreach (FieldInfo fi in fis) {
|
|
MemberMapping mmap;
|
|
string name = fi.Name + ":" + fi.DeclaringType.Name;
|
|
if (names.TryGetValue(name, out mmap)) {
|
|
namesSeen.Add(name);
|
|
object dn = InheritanceRules.DistinguishedMemberName(fi);
|
|
MetaDataMember mm;
|
|
if (!map.TryGetValue(dn, out mm)) {
|
|
mm = new MappedDataMember(this, fi, mmap, ordinal);
|
|
map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
|
|
dMembers.Add(mm);
|
|
this.InitSpecialMember(mm);
|
|
}
|
|
ValidatePrimaryKeyMember(mm);
|
|
ordinal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
PropertyInfo[] pis = TypeSystem.GetAllProperties(this.type, flags).ToArray();
|
|
if (pis != null) {
|
|
foreach (PropertyInfo pi in pis) {
|
|
MemberMapping mmap;
|
|
string name = pi.Name + ":" + pi.DeclaringType.Name;
|
|
if (names.TryGetValue(name, out mmap)) {
|
|
namesSeen.Add(name);
|
|
MetaDataMember mm;
|
|
object dn = InheritanceRules.DistinguishedMemberName(pi);
|
|
if (!map.TryGetValue(dn, out mm)) {
|
|
mm = new MappedDataMember(this, pi, mmap, ordinal);
|
|
map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
|
|
dMembers.Add(mm);
|
|
this.InitSpecialMember(mm);
|
|
}
|
|
ValidatePrimaryKeyMember(mm);
|
|
ordinal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.dataMembers = dMembers.AsReadOnly();
|
|
this.dataMemberMap = map;
|
|
|
|
// Finally, make sure that all types in the mapping file were consumed.
|
|
foreach(string name in namesSeen) {
|
|
names.Remove(name);
|
|
}
|
|
foreach(var orphan in names) {
|
|
Type aboveRoot = inheritanceRoot.Type.BaseType;
|
|
while (aboveRoot!=null) {
|
|
foreach(MemberInfo mi in aboveRoot.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
|
|
if(String.Compare(mi.Name, orphan.Value.MemberName, StringComparison.Ordinal)==0) {
|
|
throw Error.MappedMemberHadNoCorrespondingMemberInType(orphan.Value.MemberName, type.Name);
|
|
}
|
|
}
|
|
aboveRoot = aboveRoot.BaseType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void InitSpecialMember(MetaDataMember mm) {
|
|
// Can only have one auto gen member that is also an identity member,
|
|
// except if that member is a computed column (since they are implicitly auto gen)
|
|
if (mm.IsDbGenerated && mm.IsPrimaryKey && string.IsNullOrEmpty(mm.Expression)) {
|
|
if (this.dbGeneratedIdentity != null) {
|
|
throw Error.TwoMembersMarkedAsPrimaryKeyAndDBGenerated(mm.Member, this.dbGeneratedIdentity.Member);
|
|
}
|
|
this.dbGeneratedIdentity = mm;
|
|
}
|
|
if (mm.IsPrimaryKey && !MappingSystem.IsSupportedIdentityType(mm.Type))
|
|
{
|
|
throw Error.IdentityClrTypeNotSupported(mm.DeclaringType, mm.Name, mm.Type);
|
|
}
|
|
if (mm.IsVersion) {
|
|
if (this.version != null) {
|
|
throw Error.TwoMembersMarkedAsRowVersion(mm.Member, this.version.Member);
|
|
}
|
|
this.version = mm;
|
|
}
|
|
if (mm.IsDiscriminator) {
|
|
if (this.discriminator != null) {
|
|
if (!InheritanceRules.AreSameMember(this.discriminator.Member, mm.Member)) {
|
|
throw Error.TwoMembersMarkedAsInheritanceDiscriminator(mm.Member, this.discriminator.Member);
|
|
}
|
|
}
|
|
else {
|
|
this.discriminator = mm;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
public override MetaModel Model {
|
|
get { return this.model; }
|
|
}
|
|
public override MetaTable Table {
|
|
get { return this.table; }
|
|
}
|
|
public override Type Type {
|
|
get { return this.type; }
|
|
}
|
|
public override string Name {
|
|
get { return this.type.Name; }
|
|
}
|
|
public override bool IsEntity {
|
|
get {
|
|
if (this.table != null) {
|
|
return table.RowType.IdentityMembers.Count > 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
public override bool CanInstantiate {
|
|
get { return !this.type.IsAbstract && (this == this.InheritanceRoot || this.HasInheritanceCode); }
|
|
}
|
|
public override MetaDataMember DBGeneratedIdentityMember {
|
|
get { return this.dbGeneratedIdentity; }
|
|
}
|
|
public override MetaDataMember VersionMember {
|
|
get { return this.version; }
|
|
}
|
|
public override MetaDataMember Discriminator {
|
|
get { return this.discriminator; }
|
|
}
|
|
public override bool HasUpdateCheck {
|
|
get {
|
|
foreach (MetaDataMember member in this.PersistentDataMembers) {
|
|
if (member.UpdateCheck != UpdateCheck.Never) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
public override bool HasInheritance {
|
|
get { return this.inheritanceRoot.HasInheritance; }
|
|
}
|
|
public override object InheritanceCode {
|
|
get { return this.inheritanceCode; }
|
|
}
|
|
public override bool HasInheritanceCode {
|
|
get { return this.InheritanceCode != null; }
|
|
}
|
|
public override bool IsInheritanceDefault {
|
|
get { return this.InheritanceDefault == this; }
|
|
}
|
|
public override MetaType InheritanceDefault {
|
|
get {
|
|
if (this.inheritanceRoot == this)
|
|
throw Error.CannotGetInheritanceDefaultFromNonInheritanceClass();
|
|
return this.InheritanceRoot.InheritanceDefault;
|
|
}
|
|
}
|
|
public override MetaType InheritanceRoot {
|
|
get { return this.inheritanceRoot; }
|
|
}
|
|
public override MetaType InheritanceBase {
|
|
get {
|
|
// LOCKING: Cannot initialize at construction
|
|
if (!this.inheritanceBaseSet && this.inheritanceBase == null) {
|
|
lock (this.locktarget) {
|
|
if (this.inheritanceBase == null) {
|
|
this.inheritanceBase = InheritanceBaseFinder.FindBase(this);
|
|
this.inheritanceBaseSet = true;
|
|
}
|
|
}
|
|
}
|
|
return this.inheritanceBase;
|
|
}
|
|
}
|
|
public override ReadOnlyCollection<MetaType> InheritanceTypes {
|
|
get { return this.inheritanceRoot.InheritanceTypes; }
|
|
}
|
|
public override ReadOnlyCollection<MetaType> DerivedTypes {
|
|
get {
|
|
// LOCKING: Cannot initialize at construction because derived types
|
|
// won't exist yet.
|
|
if (this.derivedTypes == null) {
|
|
lock (this.locktarget) {
|
|
if (this.derivedTypes == null) {
|
|
List<MetaType> dTypes = new List<MetaType>();
|
|
foreach (MetaType mt in this.InheritanceTypes) {
|
|
if (mt.Type.BaseType == this.type)
|
|
dTypes.Add(mt);
|
|
}
|
|
this.derivedTypes = dTypes.AsReadOnly();
|
|
}
|
|
}
|
|
}
|
|
return this.derivedTypes;
|
|
}
|
|
}
|
|
public override MetaType GetInheritanceType(Type inheritanceType) {
|
|
foreach (MetaType mt in this.InheritanceTypes)
|
|
if (mt.Type == inheritanceType)
|
|
return mt;
|
|
return null;
|
|
}
|
|
public override MetaType GetTypeForInheritanceCode(object key) {
|
|
if (this.InheritanceRoot.Discriminator.Type == typeof(string)) {
|
|
string skey = (string)key;
|
|
foreach (MetaType mt in this.InheritanceRoot.InheritanceTypes) {
|
|
if (string.Compare((string)mt.InheritanceCode, skey, StringComparison.OrdinalIgnoreCase) == 0)
|
|
return mt;
|
|
}
|
|
}
|
|
else {
|
|
foreach (MetaType mt in this.InheritanceRoot.InheritanceTypes) {
|
|
if (object.Equals(mt.InheritanceCode, key))
|
|
return mt;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> DataMembers {
|
|
get { return this.dataMembers; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> PersistentDataMembers {
|
|
get { return this.persistentDataMembers; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> IdentityMembers {
|
|
get { return this.identities; }
|
|
}
|
|
public override ReadOnlyCollection<MetaAssociation> Associations {
|
|
get {
|
|
// LOCKING: Associations are late-expanded so that cycles are broken.
|
|
if (this.associations == null) {
|
|
lock (this.locktarget) {
|
|
if (this.associations == null) {
|
|
this.associations = this.dataMembers.Where(m => m.IsAssociation).Select(m => m.Association).ToList().AsReadOnly();
|
|
}
|
|
}
|
|
}
|
|
return this.associations;
|
|
}
|
|
}
|
|
public override MetaDataMember GetDataMember(MemberInfo mi) {
|
|
if (mi == null)
|
|
throw Error.ArgumentNull("mi");
|
|
MetaDataMember mm;
|
|
if (this.dataMemberMap.TryGetValue(InheritanceRules.DistinguishedMemberName(mi), out mm)) {
|
|
return mm;
|
|
} else {
|
|
if (mi.DeclaringType.IsInterface) {
|
|
throw Error.MappingOfInterfacesMemberIsNotSupported(mi.DeclaringType.Name, mi.Name);
|
|
} else { //the member is not mapped in the base class
|
|
throw Error.UnmappedClassMember(mi.DeclaringType.Name, mi.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override MethodInfo OnLoadedMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.onLoadedMethod;
|
|
}
|
|
}
|
|
|
|
public override MethodInfo OnValidateMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.onValidateMethod;
|
|
}
|
|
}
|
|
public override bool HasAnyValidateMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.hasAnyValidateMethod;
|
|
}
|
|
}
|
|
public override bool HasAnyLoadMethod {
|
|
get {
|
|
this.InitMethods();
|
|
return this.hasAnyLoadMethod;
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
return this.Name;
|
|
}
|
|
}
|
|
|
|
internal sealed class MappedDataMember : MetaDataMember {
|
|
MetaType declaringType;
|
|
MemberInfo member;
|
|
MemberInfo storageMember;
|
|
int ordinal;
|
|
Type type;
|
|
bool hasAccessors;
|
|
MetaAccessor accPublic;
|
|
MetaAccessor accPrivate;
|
|
MetaAccessor accDefValue;
|
|
MetaAccessor accDefSource;
|
|
MemberMapping memberMap;
|
|
MappedAssociation assoc;
|
|
bool isNullableType;
|
|
bool isDeferred;
|
|
bool isPrimaryKey;
|
|
bool isVersion;
|
|
bool isDBGenerated;
|
|
bool isDiscriminator;
|
|
bool canBeNull = true;
|
|
string dbType;
|
|
string expression;
|
|
string mappedName;
|
|
UpdateCheck updateCheck = UpdateCheck.Never;
|
|
AutoSync autoSync = AutoSync.Never;
|
|
object locktarget = new object(); // Hold locks on private object rather than public MetaType.
|
|
bool hasLoadMethod;
|
|
MethodInfo loadMethod;
|
|
|
|
internal MappedDataMember(MetaType declaringType, MemberInfo mi, MemberMapping map, int ordinal) {
|
|
this.declaringType = declaringType;
|
|
this.member = mi;
|
|
this.ordinal = ordinal;
|
|
this.type = TypeSystem.GetMemberType(mi);
|
|
this.isNullableType = TypeSystem.IsNullableType(this.type);
|
|
this.memberMap = map;
|
|
if (this.memberMap != null && this.memberMap.StorageMemberName != null) {
|
|
MemberInfo[] mis = mi.DeclaringType.GetMember(this.memberMap.StorageMemberName, BindingFlags.Instance | BindingFlags.NonPublic);
|
|
if (mis == null || mis.Length != 1) {
|
|
throw Error.BadStorageProperty(this.memberMap.StorageMemberName, mi.DeclaringType, mi.Name);
|
|
}
|
|
this.storageMember = mis[0];
|
|
}
|
|
Type storageType = this.storageMember != null ? TypeSystem.GetMemberType(this.storageMember) : this.type;
|
|
this.isDeferred = IsDeferredType(storageType);
|
|
ColumnMapping cmap = map as ColumnMapping;
|
|
if (cmap != null && cmap.IsDbGenerated && cmap.IsPrimaryKey) {
|
|
// auto-gen identities must be synced on insert
|
|
if ((cmap.AutoSync != AutoSync.Default) && (cmap.AutoSync != AutoSync.OnInsert)) {
|
|
throw Error.IncorrectAutoSyncSpecification(mi.Name);
|
|
}
|
|
}
|
|
if (cmap != null) {
|
|
this.isPrimaryKey = cmap.IsPrimaryKey;
|
|
this.isVersion = cmap.IsVersion;
|
|
this.isDBGenerated = cmap.IsDbGenerated || !string.IsNullOrEmpty(cmap.Expression) || this.isVersion;
|
|
this.isDiscriminator = cmap.IsDiscriminator;
|
|
this.canBeNull = cmap.CanBeNull == null ? this.isNullableType || !this.type.IsValueType : (bool)cmap.CanBeNull;
|
|
this.dbType = cmap.DbType;
|
|
this.expression = cmap.Expression;
|
|
this.updateCheck = cmap.UpdateCheck;
|
|
// auto-gen keys are always and only synced on insert
|
|
if (this.IsDbGenerated && this.IsPrimaryKey) {
|
|
this.autoSync = AutoSync.OnInsert;
|
|
}
|
|
else if (cmap.AutoSync != AutoSync.Default) {
|
|
// if the user has explicitly set it, use their value
|
|
this.autoSync = cmap.AutoSync;
|
|
}
|
|
else if (this.IsDbGenerated) {
|
|
// database generated members default to always
|
|
this.autoSync = AutoSync.Always;
|
|
}
|
|
}
|
|
this.mappedName = this.memberMap.DbName != null ? this.memberMap.DbName : this.member.Name;
|
|
}
|
|
private void InitAccessors() {
|
|
if (!this.hasAccessors) {
|
|
lock (this.locktarget) {
|
|
if (!this.hasAccessors) {
|
|
if (this.storageMember != null) {
|
|
this.accPrivate = MakeMemberAccessor(this.member.ReflectedType, this.storageMember, null);
|
|
if (this.isDeferred) {
|
|
MakeDeferredAccessors(this.member.ReflectedType, this.accPrivate, out this.accPrivate, out this.accDefValue, out this.accDefSource);
|
|
}
|
|
this.accPublic = MakeMemberAccessor(this.member.ReflectedType, this.member, this.accPrivate);
|
|
}
|
|
else {
|
|
this.accPublic = this.accPrivate = MakeMemberAccessor(this.member.ReflectedType, this.member, null);
|
|
if (this.isDeferred) {
|
|
MakeDeferredAccessors(this.member.ReflectedType, this.accPrivate, out this.accPrivate, out this.accDefValue, out this.accDefSource);
|
|
}
|
|
}
|
|
this.hasAccessors = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public override MetaType DeclaringType {
|
|
get { return this.declaringType; }
|
|
}
|
|
public override bool IsDeclaredBy(MetaType metaType) {
|
|
if (metaType == null) {
|
|
throw Error.ArgumentNull("metaType");
|
|
}
|
|
return metaType.Type == this.member.DeclaringType;
|
|
}
|
|
public override MemberInfo Member {
|
|
get { return this.member; }
|
|
}
|
|
public override MemberInfo StorageMember {
|
|
get { return this.storageMember; }
|
|
}
|
|
public override string Name {
|
|
get { return this.member.Name; }
|
|
}
|
|
public override int Ordinal {
|
|
get { return this.ordinal; }
|
|
}
|
|
public override Type Type {
|
|
get { return this.type; }
|
|
}
|
|
public override MetaAccessor MemberAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accPublic;
|
|
}
|
|
}
|
|
public override MetaAccessor StorageAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accPrivate;
|
|
}
|
|
}
|
|
public override MetaAccessor DeferredValueAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accDefValue;
|
|
}
|
|
}
|
|
public override MetaAccessor DeferredSourceAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accDefSource;
|
|
}
|
|
}
|
|
public override bool IsDeferred {
|
|
get { return this.isDeferred; }
|
|
}
|
|
public override bool IsPersistent {
|
|
get { return this.memberMap != null; }
|
|
}
|
|
public override bool IsAssociation {
|
|
get { return this.memberMap is AssociationMapping; }
|
|
}
|
|
public override bool IsPrimaryKey {
|
|
get { return this.isPrimaryKey; }
|
|
}
|
|
/// <summary>
|
|
/// Returns true if the member is explicitly marked as auto gen, or if the
|
|
/// member is computed or generated by the database server.
|
|
/// </summary>
|
|
public override bool IsDbGenerated {
|
|
get { return this.isDBGenerated; }
|
|
}
|
|
public override bool IsVersion {
|
|
get { return this.isVersion; }
|
|
}
|
|
public override bool IsDiscriminator {
|
|
get { return this.isDiscriminator; }
|
|
}
|
|
public override bool CanBeNull {
|
|
get { return this.canBeNull; }
|
|
}
|
|
public override string DbType {
|
|
get { return this.dbType; }
|
|
}
|
|
public override string Expression {
|
|
get { return this.expression; }
|
|
}
|
|
public override string MappedName {
|
|
get { return this.mappedName; }
|
|
}
|
|
public override UpdateCheck UpdateCheck {
|
|
get { return this.updateCheck; }
|
|
}
|
|
public override AutoSync AutoSync {
|
|
get { return this.autoSync; }
|
|
}
|
|
public override MetaAssociation Association {
|
|
get {
|
|
if (this.IsAssociation) {
|
|
// LOCKING: This deferral isn't an optimization. It can't be done in the constructor
|
|
// because there may be loops in the association graph.
|
|
if (this.assoc == null) {
|
|
lock (this.locktarget) {
|
|
if (this.assoc == null) {
|
|
this.assoc = new MappedAssociation(this, (AssociationMapping)this.memberMap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return this.assoc;
|
|
}
|
|
}
|
|
public override MethodInfo LoadMethod {
|
|
get {
|
|
if (this.hasLoadMethod == false && this.IsDeferred) {
|
|
// defer searching for this access method until we really need to know
|
|
this.loadMethod = MethodFinder.FindMethod(
|
|
((MappedMetaModel)this.declaringType.Model).ContextType,
|
|
"Load" + this.member.Name,
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
new Type[] { this.DeclaringType.Type }
|
|
);
|
|
this.hasLoadMethod = true;
|
|
}
|
|
return this.loadMethod;
|
|
}
|
|
}
|
|
private bool IsDeferredType(Type clrType) {
|
|
if (clrType == null || clrType == typeof(object)) {
|
|
return false;
|
|
}
|
|
if (clrType.IsGenericType) {
|
|
Type gtype = clrType.GetGenericTypeDefinition();
|
|
return gtype == typeof(Link<>) ||
|
|
typeof(EntitySet<>).IsAssignableFrom(gtype) ||
|
|
typeof(EntityRef<>).IsAssignableFrom(gtype) ||
|
|
IsDeferredType(clrType.BaseType);
|
|
}
|
|
return false;
|
|
}
|
|
private static MetaAccessor MakeMemberAccessor(Type accessorType, MemberInfo mi, MetaAccessor storage) {
|
|
FieldInfo fi = mi as FieldInfo;
|
|
MetaAccessor acc = null;
|
|
if (fi != null) {
|
|
acc = FieldAccessor.Create(accessorType, fi);
|
|
}
|
|
else {
|
|
PropertyInfo pi = (PropertyInfo)mi;
|
|
acc = PropertyAccessor.Create(accessorType, pi, storage);
|
|
}
|
|
return acc;
|
|
}
|
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
|
private static void MakeDeferredAccessors(
|
|
Type declaringType, MetaAccessor accessor,
|
|
out MetaAccessor accessorValue, out MetaAccessor accessorDeferredValue, out MetaAccessor accessorDeferredSource
|
|
) {
|
|
if (accessor.Type.IsGenericType) {
|
|
Type gtype = accessor.Type.GetGenericTypeDefinition();
|
|
Type itemType = accessor.Type.GetGenericArguments()[0];
|
|
if (gtype == typeof(Link<>)) {
|
|
accessorValue = CreateAccessor(typeof(LinkValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredValue = CreateAccessor(typeof(LinkDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredSource = CreateAccessor(typeof(LinkDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
return;
|
|
}
|
|
else if (typeof(EntityRef<>).IsAssignableFrom(gtype)) {
|
|
accessorValue = CreateAccessor(typeof(EntityRefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredValue = CreateAccessor(typeof(EntityRefDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredSource = CreateAccessor(typeof(EntityRefDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
return;
|
|
}
|
|
else if (typeof(EntitySet<>).IsAssignableFrom(gtype)) {
|
|
accessorValue = CreateAccessor(typeof(EntitySetValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredValue = CreateAccessor(typeof(EntitySetDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
accessorDeferredSource = CreateAccessor(typeof(EntitySetDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
|
|
return;
|
|
}
|
|
}
|
|
throw Error.UnhandledDeferredStorageType(accessor.Type);
|
|
}
|
|
private static MetaAccessor CreateAccessor(Type accessorType, params object[] args) {
|
|
return (MetaAccessor)Activator.CreateInstance(accessorType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
|
|
}
|
|
}
|
|
|
|
internal class MappedAssociation : MetaAssociationImpl {
|
|
MappedDataMember thisMember;
|
|
MetaDataMember otherMember;
|
|
MetaType otherType;
|
|
ReadOnlyCollection<MetaDataMember> thisKey;
|
|
ReadOnlyCollection<MetaDataMember> otherKey;
|
|
bool isMany;
|
|
bool isForeignKey;
|
|
bool isNullable;
|
|
bool thisKeyIsPrimaryKey;
|
|
bool otherKeyIsPrimaryKey;
|
|
AssociationMapping assocMap;
|
|
|
|
internal MappedAssociation(MappedDataMember mm, AssociationMapping assocMap) {
|
|
this.thisMember = mm;
|
|
this.assocMap = assocMap;
|
|
this.Init();
|
|
this.InitOther();
|
|
//validate the number of ThisKey columns is the same as the number of OtherKey columns
|
|
if (this.thisKey.Count != this.otherKey.Count && this.thisKey.Count > 0 && this.otherKey.Count > 0) {
|
|
throw Error.MismatchedThisKeyOtherKey(thisMember.Name, thisMember.DeclaringType.Name);
|
|
}
|
|
}
|
|
#region Initialization
|
|
private void Init() {
|
|
this.isMany = TypeSystem.IsSequenceType(this.thisMember.Type);
|
|
this.thisKey = (this.assocMap.ThisKey != null)
|
|
? MakeKeys(this.thisMember.DeclaringType, this.assocMap.ThisKey)
|
|
: this.thisMember.DeclaringType.IdentityMembers;
|
|
// this association refers to the parent if thisKey is not our own identity
|
|
this.thisKeyIsPrimaryKey = AreEqual(this.thisKey, this.thisMember.DeclaringType.IdentityMembers);
|
|
this.isForeignKey = this.assocMap.IsForeignKey;
|
|
|
|
// if any key members are not nullable, the association is not nullable
|
|
this.isNullable = true;
|
|
foreach (MetaDataMember mm in this.thisKey) {
|
|
if (mm == null)
|
|
throw Error.UnexpectedNull("MetaDataMember");
|
|
|
|
if (!mm.CanBeNull) {
|
|
this.isNullable = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// validate DeleteOnNull specification
|
|
if (assocMap.DeleteOnNull == true) {
|
|
if (!(isForeignKey && !isMany && !isNullable)) {
|
|
throw Error.InvalidDeleteOnNullSpecification(thisMember);
|
|
}
|
|
}
|
|
}
|
|
private void InitOther() {
|
|
if (this.otherType == null) {
|
|
Type ot = this.isMany ? TypeSystem.GetElementType(this.thisMember.Type) : this.thisMember.Type;
|
|
this.otherType = this.thisMember.DeclaringType.Model.GetMetaType(ot);
|
|
System.Diagnostics.Debug.Assert(this.otherType.IsEntity);
|
|
this.otherKey = (assocMap.OtherKey != null)
|
|
? MakeKeys(this.otherType, this.assocMap.OtherKey)
|
|
: this.otherType.IdentityMembers;
|
|
this.otherKeyIsPrimaryKey = AreEqual(this.otherKey, this.otherType.IdentityMembers);
|
|
foreach (MetaDataMember omm in this.otherType.DataMembers) {
|
|
if (omm.IsAssociation && omm != this.thisMember && omm.MappedName == this.thisMember.MappedName) {
|
|
this.otherMember = omm;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
public override MetaDataMember ThisMember {
|
|
get { return this.thisMember; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> ThisKey {
|
|
get { return this.thisKey; }
|
|
}
|
|
public override MetaDataMember OtherMember {
|
|
get { return this.otherMember; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> OtherKey {
|
|
get { return this.otherKey; }
|
|
}
|
|
public override MetaType OtherType {
|
|
get { return this.otherType; }
|
|
}
|
|
public override bool IsMany {
|
|
get { return this.isMany; }
|
|
}
|
|
public override bool IsForeignKey {
|
|
get { return this.isForeignKey; }
|
|
}
|
|
public override bool IsUnique {
|
|
get { return this.assocMap.IsUnique; }
|
|
}
|
|
public override bool IsNullable {
|
|
get { return this.isNullable; }
|
|
}
|
|
public override bool ThisKeyIsPrimaryKey {
|
|
get { return this.thisKeyIsPrimaryKey; }
|
|
}
|
|
public override bool OtherKeyIsPrimaryKey {
|
|
get { return this.otherKeyIsPrimaryKey; }
|
|
}
|
|
public override string DeleteRule {
|
|
get {
|
|
return this.assocMap.DeleteRule;
|
|
}
|
|
}
|
|
public override bool DeleteOnNull {
|
|
get {
|
|
return this.assocMap.DeleteOnNull;
|
|
}
|
|
}
|
|
}
|
|
|
|
class MappedFunction : MetaFunction {
|
|
MetaModel model;
|
|
FunctionMapping map;
|
|
MethodInfo method;
|
|
ReadOnlyCollection<MetaParameter> parameters;
|
|
MetaParameter returnParameter;
|
|
ReadOnlyCollection<MetaType> rowTypes;
|
|
static ReadOnlyCollection<MetaParameter> _emptyParameters = new List<MetaParameter>(0).AsReadOnly();
|
|
static ReadOnlyCollection<MetaType> _emptyTypes = new List<MetaType>(0).AsReadOnly();
|
|
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // map parameter contains type names.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
|
|
internal MappedFunction(MappedMetaModel model, FunctionMapping map, MethodInfo method) {
|
|
this.model = model;
|
|
this.map = map;
|
|
this.method = method;
|
|
this.rowTypes = _emptyTypes;
|
|
|
|
if (map.Types.Count == 0 && this.method.ReturnType == typeof(IMultipleResults)) {
|
|
throw Error.NoResultTypesDeclaredForFunction(method.Name);
|
|
}
|
|
else if (map.Types.Count > 1 && this.method.ReturnType != typeof(IMultipleResults)) {
|
|
throw Error.TooManyResultTypesDeclaredForFunction(method.Name);
|
|
}
|
|
else if (map.Types.Count == 1 && this.method.ReturnType != typeof(IMultipleResults)) {
|
|
Type elementType = TypeSystem.GetElementType(method.ReturnType);
|
|
this.rowTypes = new List<MetaType>(1) { this.GetMetaType(map.Types[0], elementType) }.AsReadOnly();
|
|
}
|
|
else if (map.Types.Count > 0) {
|
|
List<MetaType> rowTypes = new List<MetaType>();
|
|
foreach (TypeMapping rtm in map.Types) {
|
|
Type elementType = model.FindType(rtm.Name);
|
|
if (elementType == null) {
|
|
throw Error.CouldNotFindElementTypeInModel(rtm.Name);
|
|
}
|
|
MetaType mt = this.GetMetaType(rtm, elementType);
|
|
// Only add unique meta types
|
|
if (!rowTypes.Contains(mt)) {
|
|
rowTypes.Add(mt);
|
|
}
|
|
}
|
|
this.rowTypes = rowTypes.AsReadOnly();
|
|
}
|
|
else if (map.FunReturn != null) {
|
|
this.returnParameter = new MappedReturnParameter(method.ReturnParameter, map.FunReturn);
|
|
}
|
|
|
|
// Parameters.
|
|
ParameterInfo[] pis = this.method.GetParameters();
|
|
if (pis.Length > 0) {
|
|
List<MetaParameter> mps = new List<MetaParameter>(pis.Length);
|
|
if (this.map.Parameters.Count != pis.Length) {
|
|
throw Error.IncorrectNumberOfParametersMappedForMethod(this.map.MethodName);
|
|
}
|
|
for (int i = 0; i < pis.Length; i++) {
|
|
mps.Add(new MappedParameter(pis[i], this.map.Parameters[i]));
|
|
}
|
|
this.parameters = mps.AsReadOnly();
|
|
}
|
|
else {
|
|
this.parameters = _emptyParameters;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// For the specified type, if it is a mapped type, use the Table
|
|
/// metatype to get the correct inheritance metatype,
|
|
/// otherwise create a new meta type.
|
|
[ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Parameter contains various type references.
|
|
[ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // MappedRootType constructor call.
|
|
private MetaType GetMetaType(TypeMapping tm, Type elementType) {
|
|
MetaTable tbl = model.GetTable(elementType);
|
|
if (tbl != null) {
|
|
return tbl.RowType.GetInheritanceType(elementType);
|
|
}
|
|
return new MappedRootType((MappedMetaModel)model, null, tm, elementType);
|
|
}
|
|
public override ReadOnlyCollection<MetaParameter> Parameters {
|
|
get { return this.parameters; }
|
|
}
|
|
public override string MappedName {
|
|
get { return this.map.Name; }
|
|
}
|
|
public override MethodInfo Method {
|
|
get { return this.method; }
|
|
}
|
|
public override MetaModel Model {
|
|
get { return this.model; }
|
|
}
|
|
public override string Name {
|
|
get { return this.method.Name; }
|
|
}
|
|
public override bool IsComposable {
|
|
get { return this.map.IsComposable; }
|
|
}
|
|
public override MetaParameter ReturnParameter {
|
|
get { return this.returnParameter; }
|
|
}
|
|
public override bool HasMultipleResults {
|
|
get { return this.method.ReturnType == typeof(IMultipleResults); }
|
|
}
|
|
public override ReadOnlyCollection<MetaType> ResultRowTypes {
|
|
get { return this.rowTypes; }
|
|
}
|
|
}
|
|
|
|
internal sealed class MappedParameter : MetaParameter {
|
|
private ParameterInfo parameterInfo;
|
|
private ParameterMapping map;
|
|
|
|
public MappedParameter(ParameterInfo parameterInfo, ParameterMapping map) {
|
|
this.parameterInfo = parameterInfo;
|
|
this.map = map;
|
|
}
|
|
public override ParameterInfo Parameter {
|
|
get { return this.parameterInfo; }
|
|
}
|
|
public override string Name {
|
|
get { return this.parameterInfo.Name; }
|
|
}
|
|
public override string MappedName {
|
|
get { return this.map.Name; }
|
|
}
|
|
public override Type ParameterType {
|
|
get { return this.parameterInfo.ParameterType; }
|
|
}
|
|
public override string DbType {
|
|
get { return this.map.DbType; }
|
|
}
|
|
}
|
|
|
|
internal sealed class MappedReturnParameter : MetaParameter {
|
|
private ParameterInfo parameterInfo;
|
|
private ReturnMapping map;
|
|
|
|
public MappedReturnParameter(ParameterInfo parameterInfo, ReturnMapping map) {
|
|
this.parameterInfo = parameterInfo;
|
|
this.map = map;
|
|
}
|
|
public override ParameterInfo Parameter {
|
|
get { return this.parameterInfo; }
|
|
}
|
|
public override string Name {
|
|
get { return null; }
|
|
}
|
|
public override string MappedName {
|
|
get { return null; }
|
|
}
|
|
public override Type ParameterType {
|
|
get { return this.parameterInfo.ParameterType; }
|
|
}
|
|
public override string DbType {
|
|
get { return this.map.DbType; }
|
|
}
|
|
}
|
|
|
|
internal abstract class MetaAssociationImpl : MetaAssociation {
|
|
|
|
private static char[] keySeparators = new char[] { ',' };
|
|
/// <summary>
|
|
/// Given a MetaType and a set of key fields, return the set of MetaDataMembers
|
|
/// corresponding to the key.
|
|
/// </summary>
|
|
protected static ReadOnlyCollection<MetaDataMember> MakeKeys(MetaType mtype, string keyFields) {
|
|
string[] names = keyFields.Split(keySeparators);
|
|
MetaDataMember[] members = new MetaDataMember[names.Length];
|
|
for (int i = 0; i < names.Length; i++) {
|
|
names[i] = names[i].Trim();
|
|
MemberInfo[] rmis = mtype.Type.GetMember(names[i], BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
if (rmis == null || rmis.Length != 1) {
|
|
throw Error.BadKeyMember(names[i], keyFields, mtype.Name);
|
|
}
|
|
members[i] = mtype.GetDataMember(rmis[0]);
|
|
if (members[i] == null) {
|
|
throw Error.BadKeyMember(names[i], keyFields, mtype.Name);
|
|
}
|
|
}
|
|
return new List<MetaDataMember>(members).AsReadOnly();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compare two sets of keys for equality.
|
|
/// </summary>
|
|
protected static bool AreEqual(IEnumerable<MetaDataMember> key1, IEnumerable<MetaDataMember> key2) {
|
|
using (IEnumerator<MetaDataMember> e1 = key1.GetEnumerator()) {
|
|
using (IEnumerator<MetaDataMember> e2 = key2.GetEnumerator()) {
|
|
bool m1, m2;
|
|
for (m1 = e1.MoveNext(), m2 = e2.MoveNext(); m1 && m2; m1 = e1.MoveNext(), m2 = e2.MoveNext()) {
|
|
if (e1.Current != e2.Current)
|
|
return false;
|
|
}
|
|
if (m1 != m2)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return string.Format(Globalization.CultureInfo.InvariantCulture, "{0} ->{1} {2}", ThisMember.DeclaringType.Name, IsMany ? "*" : "", OtherType.Name);
|
|
}
|
|
}
|
|
|
|
internal sealed class UnmappedType : MetaType {
|
|
MetaModel model;
|
|
Type type;
|
|
Dictionary<object, MetaDataMember> dataMemberMap;
|
|
ReadOnlyCollection<MetaDataMember> dataMembers;
|
|
ReadOnlyCollection<MetaType> inheritanceTypes;
|
|
object locktarget = new object(); // Hold locks on private object rather than public MetaType.
|
|
|
|
private static ReadOnlyCollection<MetaType> _emptyTypes = new List<MetaType>().AsReadOnly();
|
|
private static ReadOnlyCollection<MetaDataMember> _emptyDataMembers = new List<MetaDataMember>().AsReadOnly();
|
|
private static ReadOnlyCollection<MetaAssociation> _emptyAssociations = new List<MetaAssociation>().AsReadOnly();
|
|
|
|
internal UnmappedType(MetaModel model, Type type) {
|
|
this.model = model;
|
|
this.type = type;
|
|
}
|
|
|
|
public override MetaModel Model {
|
|
get { return this.model; }
|
|
}
|
|
public override MetaTable Table {
|
|
get { return null; }
|
|
}
|
|
public override Type Type {
|
|
get { return this.type; }
|
|
}
|
|
public override string Name {
|
|
get { return this.type.Name; }
|
|
}
|
|
public override bool IsEntity {
|
|
get { return false; }
|
|
}
|
|
public override bool CanInstantiate {
|
|
get { return !this.type.IsAbstract; }
|
|
}
|
|
public override MetaDataMember DBGeneratedIdentityMember {
|
|
get { return null; }
|
|
}
|
|
public override MetaDataMember VersionMember {
|
|
get { return null; }
|
|
}
|
|
public override MetaDataMember Discriminator {
|
|
get { return null; }
|
|
}
|
|
public override bool HasUpdateCheck {
|
|
get { return false; }
|
|
}
|
|
public override ReadOnlyCollection<MetaType> InheritanceTypes {
|
|
get {
|
|
if (this.inheritanceTypes == null) {
|
|
lock (this.locktarget) {
|
|
if (this.inheritanceTypes == null) {
|
|
this.inheritanceTypes = new MetaType[] { this }.ToList().AsReadOnly();
|
|
}
|
|
}
|
|
}
|
|
return this.inheritanceTypes;
|
|
}
|
|
}
|
|
public override MetaType GetInheritanceType(Type inheritanceType) {
|
|
if (inheritanceType == this.type)
|
|
return this;
|
|
return null;
|
|
}
|
|
public override ReadOnlyCollection<MetaType> DerivedTypes {
|
|
get { return _emptyTypes; }
|
|
}
|
|
public override MetaType GetTypeForInheritanceCode(object key) {
|
|
return null;
|
|
}
|
|
public override bool HasInheritance {
|
|
get { return false; }
|
|
}
|
|
public override bool HasInheritanceCode {
|
|
get { return false; }
|
|
}
|
|
public override object InheritanceCode {
|
|
get { return null; }
|
|
}
|
|
public override MetaType InheritanceRoot {
|
|
get { return this; }
|
|
}
|
|
public override MetaType InheritanceBase {
|
|
get { return null; }
|
|
}
|
|
public override MetaType InheritanceDefault {
|
|
get { return null; }
|
|
}
|
|
public override bool IsInheritanceDefault {
|
|
get { return false; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> DataMembers {
|
|
get {
|
|
this.InitDataMembers();
|
|
return this.dataMembers;
|
|
}
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> PersistentDataMembers {
|
|
get { return _emptyDataMembers; }
|
|
}
|
|
public override ReadOnlyCollection<MetaDataMember> IdentityMembers {
|
|
get {
|
|
this.InitDataMembers();
|
|
return this.dataMembers;
|
|
}
|
|
}
|
|
public override ReadOnlyCollection<MetaAssociation> Associations {
|
|
get { return _emptyAssociations; }
|
|
}
|
|
public override MetaDataMember GetDataMember(MemberInfo mi) {
|
|
if (mi == null)
|
|
throw Error.ArgumentNull("mi");
|
|
this.InitDataMembers();
|
|
if (this.dataMemberMap == null) {
|
|
lock (this.locktarget) {
|
|
if (this.dataMemberMap == null) {
|
|
Dictionary<object, MetaDataMember> map = new Dictionary<object, MetaDataMember>();
|
|
foreach (MetaDataMember mm in this.dataMembers) {
|
|
map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
|
|
}
|
|
this.dataMemberMap = map;
|
|
}
|
|
}
|
|
}
|
|
object dn = InheritanceRules.DistinguishedMemberName(mi);
|
|
MetaDataMember mdm;
|
|
this.dataMemberMap.TryGetValue(dn, out mdm);
|
|
return mdm;
|
|
}
|
|
|
|
private void InitDataMembers() {
|
|
if (this.dataMembers == null) {
|
|
lock (this.locktarget) {
|
|
if (this.dataMembers == null) {
|
|
List<MetaDataMember> dMembers = new List<MetaDataMember>();
|
|
int ordinal = 0;
|
|
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
|
|
foreach (FieldInfo fi in this.type.GetFields(flags)) {
|
|
MetaDataMember mm = new UnmappedDataMember(this, fi, ordinal);
|
|
dMembers.Add(mm);
|
|
ordinal++;
|
|
}
|
|
foreach (PropertyInfo pi in this.type.GetProperties(flags)) {
|
|
MetaDataMember mm = new UnmappedDataMember(this, pi, ordinal);
|
|
dMembers.Add(mm);
|
|
ordinal++;
|
|
}
|
|
this.dataMembers = dMembers.AsReadOnly();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
return this.Name;
|
|
}
|
|
|
|
public override MethodInfo OnLoadedMethod {
|
|
get { return null; }
|
|
}
|
|
|
|
public override MethodInfo OnValidateMethod {
|
|
get { return null; }
|
|
}
|
|
public override bool HasAnyValidateMethod {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
public override bool HasAnyLoadMethod {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class UnmappedDataMember : MetaDataMember {
|
|
MetaType declaringType;
|
|
MemberInfo member;
|
|
int ordinal;
|
|
Type type;
|
|
MetaAccessor accPublic;
|
|
object lockTarget = new object();
|
|
|
|
internal UnmappedDataMember(MetaType declaringType, MemberInfo mi, int ordinal) {
|
|
this.declaringType = declaringType;
|
|
this.member = mi;
|
|
this.ordinal = ordinal;
|
|
this.type = TypeSystem.GetMemberType(mi);
|
|
}
|
|
private void InitAccessors() {
|
|
if (this.accPublic == null) {
|
|
lock (this.lockTarget) {
|
|
if (this.accPublic == null) {
|
|
this.accPublic = MakeMemberAccessor(this.member.ReflectedType, this.member);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public override MetaType DeclaringType {
|
|
get { return this.declaringType; }
|
|
}
|
|
public override bool IsDeclaredBy(MetaType metaType) {
|
|
if (metaType == null) {
|
|
throw Error.ArgumentNull("metaType");
|
|
}
|
|
return metaType.Type == this.member.DeclaringType;
|
|
}
|
|
public override MemberInfo Member {
|
|
get { return this.member; }
|
|
}
|
|
public override MemberInfo StorageMember {
|
|
get { return this.member; }
|
|
}
|
|
public override string Name {
|
|
get { return this.member.Name; }
|
|
}
|
|
public override int Ordinal {
|
|
get { return this.ordinal; }
|
|
}
|
|
public override Type Type {
|
|
get { return this.type; }
|
|
}
|
|
public override MetaAccessor MemberAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accPublic;
|
|
}
|
|
}
|
|
public override MetaAccessor StorageAccessor {
|
|
get {
|
|
this.InitAccessors();
|
|
return this.accPublic;
|
|
}
|
|
}
|
|
public override MetaAccessor DeferredValueAccessor {
|
|
get { return null; }
|
|
}
|
|
public override MetaAccessor DeferredSourceAccessor {
|
|
get { return null; }
|
|
}
|
|
public override bool IsDeferred {
|
|
get { return false; }
|
|
}
|
|
public override bool IsPersistent {
|
|
get { return false; }
|
|
}
|
|
public override bool IsAssociation {
|
|
get { return false; }
|
|
}
|
|
public override bool IsPrimaryKey {
|
|
get { return false; }
|
|
}
|
|
public override bool IsDbGenerated {
|
|
get { return false; }
|
|
}
|
|
public override bool IsVersion {
|
|
get { return false; }
|
|
}
|
|
public override bool IsDiscriminator {
|
|
get { return false; }
|
|
}
|
|
public override bool CanBeNull {
|
|
get { return !this.type.IsValueType || TypeSystem.IsNullableType(this.type); }
|
|
}
|
|
public override string DbType {
|
|
get { return null; }
|
|
}
|
|
public override string Expression {
|
|
get { return null; }
|
|
}
|
|
public override string MappedName {
|
|
get { return this.member.Name; }
|
|
}
|
|
public override UpdateCheck UpdateCheck {
|
|
get { return UpdateCheck.Never; }
|
|
}
|
|
public override AutoSync AutoSync {
|
|
get { return AutoSync.Never; }
|
|
}
|
|
public override MetaAssociation Association {
|
|
get { return null; }
|
|
}
|
|
public override MethodInfo LoadMethod {
|
|
get { return null; }
|
|
}
|
|
private static MetaAccessor MakeMemberAccessor(Type accessorType, MemberInfo mi) {
|
|
FieldInfo fi = mi as FieldInfo;
|
|
MetaAccessor acc = null;
|
|
if (fi != null) {
|
|
acc = FieldAccessor.Create(accessorType, fi);
|
|
}
|
|
else {
|
|
PropertyInfo pi = (PropertyInfo)mi;
|
|
acc = PropertyAccessor.Create(accessorType, pi, null);
|
|
}
|
|
return acc;
|
|
}
|
|
}
|
|
}
|