/// <summary> /// Returns a collection of members that are mapable, /// meaning they are fields or properties, public, non read-only, and non-static. /// Does not include inherited members except for the primary keys, which should be mapped with every DataType. /// Does include collection members. /// </summary> public static IEnumerable <MemberInfo> GetMappableMembers(Type type) { //look for mappable properties foreach (PropertyInfo memberInfo in type.GetAllMemberInfos().Where(m => m is PropertyInfo)) { //ignore readonly properties and fields if (memberInfo.GetMethod == null || memberInfo.SetMethod == null || !memberInfo.SetMethod.IsPublic || !memberInfo.CanWrite || !memberInfo.CanRead || MemberExpression.IsReadOnly(memberInfo) || MemberExpression.IsIndexer(memberInfo)) { continue; } //ignore "overwrite" properties to avoid duplicates with parent declarations MethodInfo baseVirtualMethod = memberInfo.GetMethod.GetRuntimeBaseDefinition(); if (baseVirtualMethod != null && baseVirtualMethod.DeclaringType != memberInfo.GetMethod.DeclaringType) { continue; } //only return an inherited member if it is a primary key if (memberInfo.DeclaringType != type && !DataMember.IsPrimaryKey(memberInfo)) { continue; } yield return(memberInfo); } //look for mappable fields foreach (FieldInfo memberInfo in type.GetAllMemberInfos().Where(m => m is FieldInfo)) { //ignore readonly properties and fields, except for collections if (!memberInfo.IsPublic || MemberExpression.IsReadOnly(memberInfo)) { continue; } //only return an inherited member if it is a primary key if (memberInfo.DeclaringType != type && !DataMember.IsPrimaryKey(memberInfo)) { continue; } yield return(memberInfo); } }
/// <summary> /// Creates a list of new DataTypes, creating as well a list of new Tables with all members of type as columns /// </summary> public static IEnumerable <DataType> DefaultMap(IEnumerable <Type> types) { //we will store here all types that are actually persistent List <DataType> persistentTypes = new List <DataType>(); Random random = new Random(); //map primary keys first, so we allow to foreign keys and inheritance to be correctly mapped foreach (Type type in types) { //skip enums and interfaces if (type.GetTypeInfo().IsEnum || type.GetTypeInfo().IsInterface) { continue; } //ignore types with no primary key var pk = GetMappableMembers(type).Where(m => DataMember.IsPrimaryKey(m)); if (pk.Count() == 0) { continue; } DataType dtype = new DataType(type); AllDataTypes.Add(dtype); foreach (var memberInfo in pk) { //create datamember dtype.AddMember(memberInfo.Name); } persistentTypes.Add(dtype); } foreach (DataType dtype in persistentTypes) { //create inheritance foreign keys if (dtype.BaseDataType != null) { ForeignKey foreignKey = new ForeignKey(); foreignKey.Table = dtype.Table; foreignKey.RemoteTable = dtype.BaseDataType.Table; foreignKey.Name = "FK_" + dtype.Name + "_" + dtype.BaseDataType.Name + "_" + random.Next(); //we asume that primary keys on parent and child tables have the same number and order of related columns for (int i = 0; i < dtype.PrimaryKey.Count(); i++) { DataMember pk = dtype.PrimaryKey.ToArray()[i]; DataMember basePk = dtype.BaseDataType.PrimaryKey.ToArray()[i]; foreignKey.Columns.Add(new Tuple <Column, Column>(pk.Column, basePk.Column)); } dtype.Table.ForeignKeys.Add(foreignKey); } //map non primary key members now foreach (var memberInfo in GetMappableMembers(dtype.InnerType).Where(m => !DataMember.IsPrimaryKey(m))) { Type returnType = MemberExpression.GetReturnType(memberInfo); //is this a collection of a mapped type? if so, ignore since this must be a 1-1, 1-many or many-many relationship and must be mapped somewhere else if (DataType.IsCollection(returnType) && IsMapped(returnType.GetCollectionItemType())) { continue; } //its a persistent type, with it's own table, map as a foreign key with one or more columns for the primary key if (IsMapped(returnType)) { //we asume this datatype is already mapped along with it's primery key DataType returnDataType = returnType; ForeignKey foreignKey = new ForeignKey(); foreignKey.Table = dtype.Table; foreignKey.RemoteTable = returnDataType.Table; foreignKey.Name = "FK_" + dtype.Name + "_" + memberInfo.Name + "_" + random.Next(); foreach (DataMember pk in returnDataType.PrimaryKey.ToList()) { Column column = new Column(); column.Name = memberInfo.Name + "_" + pk.Member.Expression.Replace('.', '_'); column.Table = dtype.Table; column.IsPrimaryKey = false; column.IsNullable = !RequiredValidator.IsRequired(memberInfo); column.DbType = DbTypeMapper.Parse(pk.Member.ReturnType); if (column.IsString) { column.Length = StringLengthValidator.GetMaxLength(pk.Member.FinalMemberInfo); } dtype.Table.Columns.Add(column); foreignKey.Columns.Add(new Tuple <Column, Column>(column, pk.Column)); //create datamember dtype.AddMember(memberInfo.Name + "." + pk.Member, column); } dtype.Table.ForeignKeys.Add(foreignKey); } //just map as a atomic value else { Column column = new Column(); column.Name = memberInfo.Name; column.Table = dtype.Table; column.IsNullable = !RequiredValidator.IsRequired(memberInfo); column.IsPrimaryKey = false; //create datamember DataMember dmember = dtype.AddMember(memberInfo.Name, column); //is this a regular atomic value? if (DbTypeMapper.DbTypeMap.ContainsValue(returnType) && returnType != typeof(object)) { column.DbType = DbTypeMapper.Parse(returnType); } else if (returnType.GetTypeInfo().IsEnum) { column.DbType = DbType.Int32; } //this is an non-atomic object, but its not mapped as a DataType, so we serialize it as json else { column.DbType = DbType.String; dmember.Converter = new Conversions.Json <object>(); } if (column.IsString) { column.Length = Data.Validation.StringLengthValidator.GetMaxLength(memberInfo); } dtype.Table.Columns.Add(column); } } yield return(dtype); } }