private void CloneCustomAttributes(PropertyBuilder property, EntityMemberInfo member) { foreach(var attr in member.Attributes) { CustomAttributeHandler handler; if (!_model.App.AttributeHandlers.TryGetValue(attr.GetType(), out handler)) continue; var attrBuilder = handler.Clone(attr); if (attrBuilder != null) property.SetCustomAttribute(attrBuilder); } }
public static string BinaryToString(EntityMemberInfo member, object value) { if (value == null) return string.Empty; var bv = (Binary)value; return Convert.ToBase64String(bv.ToArray()); }
public DbColumnInfo(EntityMemberInfo member, DbTableInfo table, string columnName, DbTypeInfo typeInfo) : base(table.DbModel, table.Schema, DbObjectType.Column, member) { Member = member; Table = table; SetName(columnName); TypeInfo = typeInfo; Table.Columns.Add(this); if (member.Flags.IsSet(EntityMemberFlags.Nullable)) { Flags |= DbColumnFlags.Nullable; } if (member.Flags.IsSet(EntityMemberFlags.Identity)) { Flags |= DbColumnFlags.Identity; } if (member.Flags.IsSet(EntityMemberFlags.ForeignKey)) { Flags |= DbColumnFlags.ForeignKey; if (member.ForeignKeyOwner.ReferenceInfo.ToKey.Entity.Flags.IsSet(EntityFlags.HasIdentity)) { Flags |= DbColumnFlags.IdentityForeignKey; } } if (_sizableTypes.Contains(member.DataType)) { Flags |= DbColumnFlags.UseParamForLongValues; } }
public static object BinaryFromString(EntityMemberInfo member, string value) { if (string.IsNullOrEmpty(value)) return DBNull.Value; var bytes = Convert.FromBase64String(value); return new Binary(bytes); }
private void BuildEntityMembers() { foreach (var entInfo in Model.Entities) { bool isTable = entInfo.Kind == EntityKind.Table; var props = entInfo.EntityType.GetAllProperties(); EntityMemberInfo member; foreach (var prop in props) { EntityMemberKind kind; if (TryGetMemberKind(entInfo, prop, out kind)) { member = new EntityMemberInfo(entInfo, kind, prop); //member is added to EntityInfo.Members automatically member.Size = member.GetDefaultMemberSize(); var memberAttrs = prop.GetAllAttributes(); // if it is table, or prop is declared on THIS entity (not inherited), take all attributes if (isTable || prop.DeclaringType == entInfo.EntityType) { member.Attributes.AddRange(memberAttrs); } else { // for inherited members on views we take only OneToMany and ManyToMany attributes; // - we want to allow list properties on view entities (see vFictionBook view in Sample book store) var listAttrs = memberAttrs.Where(a => a is ManyToManyAttribute || a is OneToManyAttribute).ToList(); member.Attributes.AddRange(listAttrs); } } //else - (kind not found) - do nothing, the TryGetMemberKind should have logged the message } } //foreach entType }
object GetValueInterceptor(EntityRecord record, EntityMemberInfo member) { var value = _defaultGetter(record, member); var utcValue = ToUtc(value); return(utcValue); }
//Utilities private static void MarkTargetToClearLists(EntityRecord record, EntityMemberInfo member, object newEntityRef) { //If record is not new, mark old ref to clear lists if (record.Status != EntityStatus.New) { EntityRecord oldTargetRec; var oldTarget = record.ValuesTransient[member.ValueIndex]; if (oldTarget == null) { oldTargetRec = GetEntityRefTarget(record, member); } else { oldTargetRec = EntityHelper.GetRecord(oldTarget); } if (oldTargetRec != null) { oldTargetRec.MarkForClearLists(); } } //Check new ref if (newEntityRef != null) { var newTargetRec = EntityHelper.GetRecord(newEntityRef); if (newTargetRec != null) { newTargetRec.MarkForClearLists(); } } }//method
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { base.Apply(context, attribute, member); _member = member; _hashedMember = member.Entity.GetMember(this.PropertyName); if (string.IsNullOrEmpty(PropertyName)) { context.Log.Error("HashFor attribute - PropertyName must be specified. Entity/property: {0}.{1}.", member.Entity.Name, member.MemberName); return; } if (_member.DataType != typeof(int)) { context.Log.Error("HashFor attribute can be used only on int properties. Entity/property: {0}.{1}.", member.Entity.Name, member.MemberName); return; } if (_hashedMember == null) { context.Log.Error("Property {0} referenced in HashFor attribute on property {1} not found on entity {2}.", PropertyName, member.MemberName, member.Entity.Name); return; } if (_hashedMember.DataType != typeof(string)) { context.Log.Error("HashFor attribute on property {0}.{1}: target property must be of string type.", member.Entity.Name, member.MemberName); return; } _oldSetter = _hashedMember.SetValueRef; _hashedMember.SetValueRef = OnSettingValue; }
public override void ApplyOnMember(EntityModelBuilder builder) { if (HostMember.DataType != typeof(int)) { builder.Log.Error("HashFor attribute can be used only on int properties. Entity/property: {0}.{1}.", HostEntity.Name, HostMember.MemberName); return; } _hashedMember = HostEntity.GetMember(this.PropertyName); if (_hashedMember == null) { builder.Log.Error("Property {0} referenced in HashFor attribute on property {1} not found on entity {2}.", this.PropertyName, HostMember.MemberName, HostEntity.Name); return; } if (_hashedMember.DataType != typeof(string)) { builder.Log.Error("HashFor attribute on property {0}.{1}: target property must be of string type.", HostEntity.Name, HostMember.MemberName); return; } _oldSetter = _hashedMember.SetValueRef; _hashedMember.SetValueRef = OnSettingValue; _hashingService = builder.Model.App.GetService <IHashingService>(); }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { var entity = member.Entity; var namesArr = StringHelper.SplitNames(this.MemberNames); foreach (var name in namesArr) { var targetMember = entity.GetMember(name); if (targetMember == null) { context.Log.Error("Member {0} referenced in DependsOn attribute on member {1}.{2} not found.", name, entity.Name, member.MemberName); continue; } //add this member to DependentMembers array of targetMember if (targetMember.DependentMembers == null) { targetMember.DependentMembers = new EntityMemberInfo[] { member } } ; else { var mList = targetMember.DependentMembers.ToList(); mList.Add(member); targetMember.DependentMembers = mList.ToArray(); } } //foreach name } // method
protected virtual DbSpecialType GetDbSpecialType(Type colType, EntityMemberInfo forMember) { if (colType == typeof(string)) { var colAttr = forMember.GetAttribute <ColumnAttribute>(); var ansi = colAttr != null && colAttr.AnsiString; var unlimited = forMember.Size < 0; if (ansi) { return(unlimited ? DbSpecialType.StringAnsiUnlimited : DbSpecialType.StringAnsi); } else { return(unlimited ? DbSpecialType.StringUnlimited : DbSpecialType.String); } } else if (colType == typeof(byte[])) { return(forMember.Size < 0 ? DbSpecialType.BinaryUnlimited : DbSpecialType.Binary); } else { return(DbSpecialType.None); } }
public virtual DbTypeInfo GetDbTypeInfo(EntityMemberInfo forMember) { var colType = forMember.DataType.GetUnderlyingStorageClrType(); if (DbTypesByClrType.TryGetValue(colType, out var typeInfo)) { return(typeInfo); } // direct mapping by col type (CLR type) not found. DbTypeDef storageType = null; // find by value kind var mappedTo = GetDbSpecialType(colType, forMember); if (mappedTo != DbSpecialType.None) { SpecialTypeDefs.TryGetValue(mappedTo, out storageType); } // if not found try by col type if (storageType == null) { TypeDefsByColumnOutType.TryGetValue(colType, out storageType); } if (storageType == null) { return(null); } // found storage type; try default mapping if (storageType.DefaultTypeInfo != null) { return(storageType.DefaultTypeInfo); } // create new mapping return(CreateDbTypeInfo(colType, storageType, forMember.Size, forMember.Precision, forMember.Scale)); }
}//method public static EntityRecord GetEntityRefTarget(EntityRecord record, EntityMemberInfo member) { // Current value is null (meaning not set, yet). We must figure it out. // 1. If corresponding FK is null, then value is DbNull. var fromKey = member.ReferenceInfo.FromKey; var toKey = member.ReferenceInfo.ToKey; if (!record.KeyIsLoaded(fromKey) && record.Status != EntityStatus.New) { record.Reload(); } if (record.KeyIsNull(fromKey)) { return(null); } // 2. The key is not null, let's lookup the record by FK; maybe it was already loaded var fkValue = new EntityKey(fromKey, record); var targetEntity = toKey.Entity; //Compute pkValue on target entity var pkValue = new EntityKey(targetEntity.PrimaryKey, fkValue.Values); //If Session available, ask session for existing record EntityRecord targetRec; if (record.Session != null) { targetRec = record.Session.GetRecord(pkValue, LoadFlags.Stub); //either already loaded or a stub } else { targetRec = new EntityRecord(pkValue); //create detached stub } return(targetRec); }//method
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { base.Apply(context, attribute, member); var da = (DisplayNameAttribute)attribute; member.DisplayName = da.DisplayName; }
// targetEntProp is for many-to-many list ordering, when target prop in oder list is on 'target' ent; ex: // session.EntitySet<IBookAuthor>().OrderBy(ba => ba.Author.LastName) // in this case targetEntProp => IBookAuthor.Author public static Expression AddOrderBy(Expression entSet, EntityMemberInfo member, bool desc, EntityMemberInfo targetEntProp = null) { // Special case - member is entity ref if (member.Kind == EntityMemberKind.EntityRef) { var orderedEntSet = entSet; foreach (var fkm in member.ReferenceInfo.FromKey.ExpandedKeyMembers) { orderedEntSet = AddOrderBy(orderedEntSet, fkm.Member, desc); } return(orderedEntSet); } var entType = entSet.Type.GetGenericArguments()[0]; var entPrm = Expression.Parameter(entType, "@e"); Expression ordEnt = entPrm; if (targetEntProp != null) { ordEnt = Expression.MakeMemberAccess(entPrm, targetEntProp.ClrMemberInfo); } // !! member might be 'hidden' (FK column), so we can't use Expression.MakeMemberAccess as in prior line var readProp = MakeGetProperty(ordEnt, member); var lambda = Expression.Lambda(readProp, entPrm); var orderMethod = desc ? LinqExpressionHelper.QueryableOrderByDescMethod : LinqExpressionHelper.QueryableOrderByMethod; var genOrderMethod = orderMethod.MakeGenericMethod(entType, member.DataType); return(Expression.Call(null, genOrderMethod, entSet, lambda)); }
//Assigns default Get/Set handlers public static void AssignDefaultGetSetHandlers(EntityMemberInfo member) { switch (member.Kind) { case EntityMemberKind.Column: member.GetValueRef = GetSimpleValue; member.SetValueRef = SetSimpleValue; //Special case for enum. If property is Nullable<enum>, and value coming from Db is int, then auto conversion does not work, we have to do it explicitly if (member.DataType.IsNullableValueType()) { var baseType = Nullable.GetUnderlyingType(member.DataType); if (baseType.IsEnum) { member.GetValueRef = GetNullableEnumValue; } } break; case EntityMemberKind.EntityRef: member.GetValueRef = GetEntityRefValue; member.SetValueRef = SetEntityRefValue; break; case EntityMemberKind.EntityList: member.GetValueRef = GetEntityListValue; member.SetValueRef = DummySetValue; break; }//switch }
public override DbTypeInfo GetDbTypeInfo(EntityMemberInfo member, SystemLog log) { var ti = base.GetDbTypeInfo(member, log); if (ti == null) { return(null); } var type = member.DataType; if (type.IsNullableValueType()) { type = type.GetUnderlyingType(); } if (type.IsInt()) { ti.DbType = GetDbTypeForInt(type); // Assign converter for the specific target type var conv = new IntValueConverter() { TargetType = type }; ti.ColumnToPropertyConverter = conv.ConvertValue; ti.PropertyToColumnConverter = DbValueConverter.NoConvertFunc; } return(ti); }
}//method object GetValue(EntityRecord record, EntityMemberInfo member) { var v = record.GetValueDirect(member); if (v != null) { if (v == DBNull.Value) { return(null); } var rec = (EntityRecord)v; return(rec.EntityInstance); } //retrieve entity var targetPk = EntityKey.CreateSafe(_targetEntity.PrimaryKey, record.PrimaryKey.Values); var targetRec = record.Session.GetRecord(targetPk); if (targetRec == null) { record.SetValueDirect(member, DBNull.Value); return(null); } record.SetValueDirect(member, targetRec); if (targetRec.ByRefUserPermissions == null) { targetRec.ByRefUserPermissions = member.ByRefPermissions; } return(targetRec.EntityInstance); }
public void Extend(EntityModel model) { if(model.ModelState != EntityModelState.EntitiesConstructed) return; //Add tracking properties (IDs of UserTransaction records) to all registered entities foreach(var spec in UpdateStampColumns) { foreach(var type in spec.Types) { var entInfo = model.GetEntityInfo(type); if(entInfo == null) { model.App.SystemLog.Error("Failed to find entity info for type {0}", type); continue; } if(!string.IsNullOrEmpty(spec.CreateIdPropertyName)) { var newMember = new EntityMemberInfo(entInfo, MemberKind.Column, spec.CreateIdPropertyName, typeof(Guid)); newMember.Attributes.Add(new TrackAttribute(TrackingActionType.Created)); } if(!string.IsNullOrEmpty(spec.UpdateIdPropertyName)) { var newMember = new EntityMemberInfo(entInfo, MemberKind.Column, spec.UpdateIdPropertyName, typeof(Guid)); newMember.Attributes.Add(new TrackAttribute(TrackingActionType.Updated)); } }//foreach type }// foreach spec }//method
public DbColumnInfo(EntityMemberInfo member, DbTableInfo table, string columnName, DbTypeInfo typeInfo) : base(table.DbModel, table.Schema, DbObjectType.Column, member) { Member = member; Table = table; ColumnName = columnName; TypeInfo = typeInfo; Table.Columns.Add(this); base.GlobalName = DbModelHelper.GetGlobalName(Schema, table.TableName, columnName); if (member.Flags.IsSet(EntityMemberFlags.Nullable)) { Flags |= DbColumnFlags.Nullable; } if (member.Flags.IsSet(EntityMemberFlags.Identity)) { Flags |= DbColumnFlags.Identity; } if (member.Flags.IsSet(EntityMemberFlags.ForeignKey)) { Flags |= DbColumnFlags.ForeignKey; if (member.ForeignKeyOwner.ReferenceInfo.ToKey.Entity.Flags.IsSet(EntityFlags.HasIdentity)) { Flags |= DbColumnFlags.IdentityForeignKey; } } }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { var dataType = member.DataType; if (dataType.IsGenericType) { dataType = dataType.GetGenericArguments()[0]; } bool isDateTime = dataType == typeof(DateTime) || dataType == typeof(DateTimeOffset); if (!isDateTime) { context.Log.Error("Property {0}.{1}: DateOnly attribute may be specified only on DataTime or DateTimeOffset properties. ", member.Entity.Name, member.MemberName); return; } //Inject interceptor _defaultSetter = member.SetValueRef; if (dataType == typeof(DateTime)) { member.SetValueRef = this.SetValueDateTime; } else { member.SetValueRef = this.SetValueDateTimeOffset; } }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { // Check size code and lookup in tables if (!string.IsNullOrEmpty(this.SizeCode)) { var sizeTable = context.Model.App.SizeTable; //If there is size code, look it up in SizeTable; first check module-specific value, then global value for the code int size; var fullCode = Sizes.GetFullSizeCode(member.Entity.EntityType.Namespace, this.SizeCode); if (sizeTable.TryGetValue(fullCode, out size)) //check full code with module's namespace prefix { member.Size = size; return; } if (sizeTable.TryGetValue(this.SizeCode, out size)) //check global value, non-module-specific { member.Size = size; return; } } //If size is specified explicitly, use it if (this.Size > 0) { member.Size = Size; if ((this.Options & SizeOptions.AutoTrim) != 0) { member.Flags |= EntityMemberFlags.AutoTrim; } return; } //If no Size code and no value, it is an error, lookup in module settings context.Log.Error("Property {0}.{1}: invalid Size attribute, must specify size code or value", member.Entity.Name, member.MemberName); }
public static void SetEntityRefValue(EntityRecord record, EntityMemberInfo member, object value) { //If there's list on the other side, mark target records( old ref and new ref) to clear lists. if (member.ReferenceInfo.TargetListMember != null) { MarkTargetToClearLists(record, member, value); } EntityRecord newRec = null; if (value == null) { value = DBNull.Value; } if (value == DBNull.Value) { record.ValuesTransient[member.ValueIndex] = DBNull.Value; } else { newRec = EntityHelper.GetRecord(value); Util.Check(newRec != null, "Invalid entity ref value - not an entity: {0}", value); record.ValuesTransient[member.ValueIndex] = newRec; if (newRec.ByRefUserPermissions == null) { newRec.ByRefUserPermissions = member.ByRefPermissions; } } CopyPrimaryKeyToForeign(record, member, newRec); if (record.Status == EntityStatus.Loaded) { record.Status = EntityStatus.Modified; } }
public static void AssignStringConverters(EntityMemberInfo member) { member.ValueFromStringRef = ObjectFromString; //default member.ValueToStringRef = ObjectToString; var isNullableValueType = member.DataType.IsNullableValueType(); var type = member.DataType; if (isNullableValueType) type = type.GetGenericArguments()[0]; if (type.IsEnum) { member.ValueToStringRef = EnumToString; member.ValueFromStringRef = EnumFromString; } else if (type == typeof(Guid)) { member.ValueFromStringRef = GuidFromString; } else if (type == typeof(DateTimeOffset)) { member.ValueFromStringRef = DateTimeOffsetFromString; } else if (type == typeof(DateTime)) { member.ValueToStringRef = DateTimeToString; member.ValueFromStringRef = DateTimeFromString; } else if (type == typeof(string)) { member.ValueToStringRef = StringToString; member.ValueFromStringRef = StringFromString; } else if (type == typeof(Binary)) { member.ValueToStringRef = BinaryToString; member.ValueFromStringRef = BinaryFromString; } else if (type == typeof(byte[])) { member.ValueToStringRef = BytesToString; member.ValueFromStringRef = BytesFromString; } else if (isNullableValueType) { member.ValueFromStringRef = NullableFromString; } }
// When we get the value, if it is null, we must try to load the target using foreign key values // When we set the value, we need to copy primary key values into foreign key members public static object GetEntityRefValue(EntityRecord record, EntityMemberInfo member) { var value = record.ValuesTransient[member.ValueIndex]; if (value == DBNull.Value) { return(null); } if (value != null) { //Check if record is not fantom - might happen for cached records var oldRecRef = (EntityRecord)value; if (oldRecRef.Status != EntityStatus.Fantom) { return(oldRecRef.EntityInstance); } } var rec = GetEntityRefTarget(record, member); if (rec == null) { record.ValuesTransient[member.ValueIndex] = DBNull.Value; return(null); } // rec != null if (rec.ByRefUserPermissions == null) { rec.ByRefUserPermissions = member.ByRefPermissions; } record.ValuesTransient[member.ValueIndex] = rec; return(rec.EntityInstance); }
private void ProcessEntityRefMember(EntityMemberInfo member) { // Check if EntityRef attr is present var entRefAttr = member.Attributes.OfType <EntityRefAttribute>().FirstOrDefault(); var entity = member.Entity; var propName = entity.EntityType.Name + "." + member.MemberName; var targetType = member.DataType; var targetEntity = this.Model.GetEntityInfo(targetType); if (targetEntity == null) { Log.Error("Reference property {0}: target entity not found: {1}", propName, targetType); return; } //find target key; usually it is PK on target entity, but MS SQL allows linking to any unique key // EntityRef attr might specify target unique key var targetKey = targetEntity.PrimaryKey; var targetUniqueIndexAlias = entRefAttr?.TargetUniqueIndexAlias; if (!string.IsNullOrEmpty(targetUniqueIndexAlias)) { targetKey = FindCreateKeyByAlias(targetEntity, targetUniqueIndexAlias); if (targetKey == null) { Log.Error("Property {0}: Invalid target index alias '{1}' in EntityRef attrribute, index not found on target entity {2}.", propName, targetUniqueIndexAlias, targetEntity.EntityType); return; } if (!targetKey.KeyType.IsSet(KeyType.Unique)) { Log.Error("Property {0}: Invalid target Index in EntityRef attrribute; Index {1} is not Unique.", propName, targetUniqueIndexAlias); return; } } //Create foreign key var fk = new EntityKeyInfo(entity, KeyType.ForeignKey, member); fk.IsCopyOf = targetKey; fk.KeyMembers.Add(new EntityKeyMemberInfo(member, false)); var refInfo = member.ReferenceInfo = new EntityReferenceInfo(member, fk, targetKey); // if there's EntitRef attr, apply its props if specified if (entRefAttr != null) { if (!string.IsNullOrEmpty(entRefAttr.ForeignKeyName)) { refInfo.FromKey.Name = entRefAttr.ForeignKeyName; } if (!string.IsNullOrWhiteSpace(entRefAttr.KeyColumns)) { refInfo.ForeignKeyColumns = entRefAttr.KeyColumns; } } if (targetEntity.Flags.IsSet(EntityFlags.HasIdentity)) { entity.Flags |= EntityFlags.ReferencesIdentity; } }
public void SetValueDirect(EntityMemberInfo member, object value, bool setModified = false) { var valueIndex = member.ValueIndex; if (member.Kind == EntityMemberKind.Column) { if (_status == EntityStatus.Loading) { ValuesOriginal[valueIndex] = value; } else { MaskMembersChanged.Set(member); ValuesModified[valueIndex] = value; } } else { ValuesTransient[valueIndex] = value ?? DBNull.Value; } if (member.Flags.IsSet(EntityMemberFlags.PrimaryKey)) //identity setting from insert! { this.PrimaryKey.CopyValues(this); } if (setModified && this._status == EntityStatus.Loaded) { Status = EntityStatus.Modified; } }
public void SetValue(EntityMemberInfo member, object value) { // if we are loading the record, just set the value and that's it. if (_status == EntityStatus.Loading || Session == null) { member.SetValueRef(this, member, value); return; } if (_status == EntityStatus.Stub) { this.Reload(); } var oldStatus = _status; member.SetValueRef(this, member, value); //Fire modified event if necessary if (_status != oldStatus && _status == EntityStatus.Modified) { EntityInfo.Events.OnModified(this); } if (PropertyChanged != null) { OnPropertyChanged(member.MemberName); if (member.DependentMembers != null) { for (int i = 0; i < member.DependentMembers.Length; i++) //for loop is more efficient { OnPropertyChanged(member.DependentMembers[i].MemberName); } } } }
public static void SetValueTypeReplaceDefaultWithNull(EntityRecord record, EntityMemberInfo member, object value) { if (value == member.DefaultValue) { value = DBNull.Value; } SetSimpleValue(record, member, value); }
public static object DateTimeFromString(EntityMemberInfo member, string value) { if (string.IsNullOrEmpty(value)) return DBNull.Value; var dtStyle = member.Flags.IsSet(EntityMemberFlags.Utc) ? DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal : DateTimeStyles.AssumeLocal; var result = DateTime.Parse(value, CultureInfo.InvariantCulture, dtStyle); return result; }
public bool Modified(EntityMemberInfo persistentMember) { if (persistentMember.Kind != EntityMemberKind.Column || _maskMembersChanged == null) { return(false); } return(_maskMembersChanged.IsSet(persistentMember)); }
public static object GuidFromString(EntityMemberInfo member, string value) { if (string.IsNullOrEmpty(value)) { return(DBNull.Value);// Guid.Empty; } return(Guid.Parse(value)); }
private bool ListExistsOnTargetEntity(EntityMemberInfo refMember) { Util.Check(refMember.Kind == EntityMemberKind.EntityRef, "Expected ref member."); var ent = refMember.Entity; var targetEnt = refMember.ReferenceInfo.ToKey.Entity; return(targetEnt.Members.Any(m => m.Kind == EntityMemberKind.EntityList && m.ChildListInfo.TargetEntity == ent)); }
public static object EnumFromString(EntityMemberInfo member, string value) { if (string.IsNullOrEmpty(value)) { return(0); } return(Enum.Parse(member.DataType, value, ignoreCase: true)); }
public static object BytesFromString(EntityMemberInfo member, string value) { if (string.IsNullOrEmpty(value)) { return(DBNull.Value); } return(Convert.FromBase64String(value)); }
private static Array GetMemberValues(IList<EntityRecord> records, EntityMemberInfo member) { var objArray = new HashSet<object>(records.Select(r => r.GetValueDirect(member))).Where(v => v != null && v != DBNull.Value).ToArray(); var typedArray = Array.CreateInstance(member.DataType, objArray.Length); for (int i = 0; i < typedArray.Length; i++) typedArray.SetValue(objArray[i], i); return typedArray; }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { if (!string.IsNullOrWhiteSpace(this.ColumnName)) member.ColumnName = this.ColumnName; if (!string.IsNullOrWhiteSpace(this.Default)) member.ColumnDefault = this.Default; member.Scale = this.Scale; if (this.Precision > 0) member.Precision = this.Precision; if (this.Size != 0) member.Size = this.Size; member.ExplicitDbType = this._dbType; member.ExplicitDbTypeSpec = this.DbTypeSpec; }
public override void Apply(Entities.Model.Construction.AttributeContext context, Attribute attribute, EntityMemberInfo member) { base.Apply(context, attribute, member); _member = member; if(member.DataType != typeof(Guid) && member.DataType != typeof(Guid?)) { context.Log.Error("ActivityTrack attribute may be used only on Guid properties."); return; } _member.Flags |= EntityMemberFlags.IsSystem; _defaultValue = (_member.DataType == typeof(Guid)) ? Guid.Empty : (Guid?)null; member.Entity.SaveEvents.SavingChanges += SaveEvents_SavingChanges; }
public bool Allowed(EntityMemberInfo member) { //TODO: put it into Member Masks if(member.Flags.IsSet(EntityMemberFlags.IsSystem)) return true; switch(Status) { case PermissionStatus.Denied: return false; case PermissionStatus.AllowMasked: return Mask.IsSet(member); case PermissionStatus.AllowAll: return true; } return true; }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { var linkEntityType = this.LinkEntity; var listInfo = member.ChildListInfo = new ChildEntityListInfo(member); listInfo.RelationType = EntityRelationType.ManyToMany; listInfo.LinkEntity = context.Model.GetEntityInfo(linkEntityType, true); listInfo.ParentRefMember = listInfo.LinkEntity.FindEntityRefMember(this.ThisEntityRef, member.Entity.EntityType, member, context.Log); if(listInfo.ParentRefMember == null) { context.Log.Error( "Many-to-many setup error: back reference to entity {0} not found in link entity {1}.", member.Entity.EntityType, LinkEntity); return; } listInfo.ParentRefMember.ReferenceInfo.TargetListMember = member; var targetEntType = member.DataType.GetGenericArguments()[0]; listInfo.OtherEntityRefMember = listInfo.LinkEntity.FindEntityRefMember(this.OtherEntityRef, targetEntType, member, context.Log); if (listInfo.OtherEntityRefMember != null) listInfo.TargetEntity = context.Model.GetEntityInfo(listInfo.OtherEntityRefMember.DataType, true); }
//Copies PK values into corresponding FK public static void CopyPrimaryKeyToForeign(EntityRecord record, EntityMemberInfo entityRefMember, EntityRecord refTarget) { var refInfo = entityRefMember.ReferenceInfo; var fkMembers = refInfo.FromKey.ExpandedKeyMembers; //If target is null, set all to DbNull if (refTarget == null) { for (int i = 0; i < fkMembers.Count; i++) record.SetValueDirect(fkMembers[i].Member, DBNull.Value); return; } //refTarget is not null var pkMembers = refInfo.ToKey.ExpandedKeyMembers; for (int i = 0; i < pkMembers.Count; i++) { //copy value from PK to FK member var value = refTarget.GetValueDirect(pkMembers[i].Member); record.SetValueDirect(fkMembers[i].Member, value); } }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { if (member.Kind != MemberKind.EntityRef) { context.Log.Error("EntityRef attribute may be used only on properties that are references to other entities. Property: {0}.{1}", member.Entity.Name, member.MemberName); return; } var entity = member.Entity; // Create reference info var targetType = member.DataType; //.Property.PropertyType; var targetEntity = context.Model.GetEntityInfo(targetType); Util.Check(targetEntity != null, "Target entity not found: {0}", targetType); //Create foreign key ForeignKeyName = ForeignKeyName ?? "FK_" + entity.Name + "_" + member.MemberName; var fk = new EntityKeyInfo(ForeignKeyName, KeyType.ForeignKey, entity, member); fk.KeyMembers.Add(new EntityKeyMemberInfo(member, false)); member.ReferenceInfo = new EntityReferenceInfo(member, fk, targetEntity.PrimaryKey); member.ReferenceInfo.ForeignKeyColumns = this.KeyColumns; }
public static EntityMemberInfo FindEntityRefMember(this EntityInfo inEntity, string memberNameToFind, Type typeToFind, EntityMemberInfo listMember, MemoryLog log) { IList<EntityMemberInfo> refMembers; if (!string.IsNullOrEmpty(memberNameToFind)) { refMembers = inEntity.Members.FindAll(m => m.MemberName == memberNameToFind); } else refMembers = inEntity.Members.FindAll(m => m.DataType == typeToFind || m.DataType.IsAssignableFrom(typeToFind) || typeToFind.IsAssignableFrom(m.DataType)); if (refMembers.Count == 1) return refMembers[0]; //Report error var listMemberDesc = listMember.Entity.FullName + "." + listMember.MemberName; if (refMembers.Count == 0) log.Error("EntityList member {0}: could not find matching foreign key member in target entity {1}. ", listMemberDesc, inEntity.EntityType); if (refMembers.Count > 1) log.Error("EntityList member {0}: more than one matching foreign key member in target entity {1}. ", listMemberDesc, inEntity.EntityType); return null; }
//Assigns default Get/Set handlers public static void AssignDefaultGetSetHandlers(EntityMemberInfo member) { switch (member.Kind) { case MemberKind.Column: member.GetValueRef = GetSimpleValue; member.SetValueRef = SetSimpleValue; //Special case for enum. If property is Nullable<enum>, and value coming from Db is int, then auto conversion does not work, we have to do it explicitly if (member.DataType.IsNullableValueType()) { var baseType = Nullable.GetUnderlyingType(member.DataType); if (baseType.IsEnum) member.GetValueRef = GetNullableEnumValue; } break; case MemberKind.EntityRef: member.GetValueRef = GetEntityRefValue; member.SetValueRef = SetEntityRefValue; break; case MemberKind.EntityList: member.GetValueRef = GetEntityListValue; member.SetValueRef = DummySetValue; break; }//switch }
public void Extend(EntityModel model) { if(model.ModelState != EntityModelState.EntitiesConstructed) return; //Add tracking properties (IDs of UserTransaction records) to all registered entities foreach(var spec in UpdateStampColumns) { foreach(var type in spec.Types) { var entInfo = model.GetEntityInfo(type); if(entInfo == null) { model.App.ActivationLog.Error("Failed to find entity info for type {0}", type); continue; } if(!string.IsNullOrEmpty(spec.CreateIdPropertyName)) { var newMember = new EntityMemberInfo(entInfo, MemberKind.Column, spec.CreateIdPropertyName, typeof(Guid)); newMember.Attributes.Add(new TrackAttribute(TrackingActionType.Created)); } if(!string.IsNullOrEmpty(spec.UpdateIdPropertyName)) { var newMember = new EntityMemberInfo(entInfo, MemberKind.Column, spec.UpdateIdPropertyName, typeof(Guid)); newMember.Attributes.Add(new TrackAttribute(TrackingActionType.Updated)); } }//foreach type }// foreach spec }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { var listInfo = member.ChildListInfo = new ChildEntityListInfo(member); listInfo.RelationType = EntityRelationType.ManyToOne; var entType = member.Entity.EntityType; var targetType = member.DataType.GetGenericArguments()[0]; listInfo.TargetEntity = context.Model.GetEntityInfo(targetType, true); if (!string.IsNullOrWhiteSpace(this.ThisEntityRef)) { var fkMember = listInfo.TargetEntity.GetMember(this.ThisEntityRef); if (fkMember == null) { context.Log.Error("EntityList member {0}.{1}: could not find property {2} in target entity. ", entType, member.MemberName, this.ThisEntityRef); return; } this.ThisEntityRef = fkMember.MemberName; listInfo.ParentRefMember = fkMember; } else listInfo.ParentRefMember = listInfo.TargetEntity.FindEntityRefMember(this.ThisEntityRef, entType, member, context.Log); //Check that reference is found if(listInfo.ParentRefMember == null) context.Log.Error("EntityList member {0}.{1}: could not find reference property in target entity. ", entType, member.MemberName); else //Set back reference to list from ref member listInfo.ParentRefMember.ReferenceInfo.TargetListMember = member; listInfo.Filter = this.Filter; }
public DbColumnInfo(EntityMemberInfo member, DbTableInfo table, string columnName, DbTypeInfo typeInfo) : base(table.DbModel, table.Schema, DbObjectType.Column, member) { Member = member; Table = table; ColumnName = columnName; TypeInfo = typeInfo; Table.Columns.Add(this); base.GlobalName = DbModelHelper.GetGlobalName(Schema, table.TableName, columnName); if (member.Flags.IsSet(EntityMemberFlags.Nullable)) Flags |= DbColumnFlags.Nullable; if (member.Flags.IsSet(EntityMemberFlags.Identity)) Flags |= DbColumnFlags.Identity; if (member.Flags.IsSet(EntityMemberFlags.ForeignKey)) { Flags |= DbColumnFlags.ForeignKey; if (member.ForeignKeyOwner.ReferenceInfo.ToKey.Entity.Flags.IsSet(EntityFlags.HasIdentity)) Flags |= DbColumnFlags.IdentityForeignKey; } }
private void BuildRegularProperty(TypeBuilder typeBuilder, EntityMemberInfo member) { var propBuilder = typeBuilder.DefineProperty(member.MemberName, PropertyAttributes.None, member.DataType, Type.EmptyTypes); CreateGetter(typeBuilder, propBuilder, member.Index); // Note: we create setters for all "real" properties, even if interface has only getter - to work properly with LINQ, which uses setter // to assign value coming from the database; // The only exception is Computed property - it does not need setter if (!member.Flags.IsSet(EntityMemberFlags.Computed)) CreateSetter(typeBuilder, propBuilder, member.Index); CloneCustomAttributes(propBuilder, member); }
// Example: records: List<IBook>, listMember: book.Author; so we load authors list for each book private IList<EntityRecord> RunIncludeForListManyToMany(IList<EntityRecord> records, EntityMemberInfo listMember) { var pkInfo = listMember.Entity.PrimaryKey; var expMembers = pkInfo.ExpandedKeyMembers; Util.Check(expMembers.Count == 1, "Include expression not supported for entities with composite keys, property: {0}.", listMember); //Link records var pkMember = expMembers[0].Member; // IBook.Id var pkValues = GetMemberValues(records, pkMember); var parentRefMember = listMember.ChildListInfo.ParentRefMember; //IBookAuthor.Book var parentRefKey = parentRefMember.ReferenceInfo.FromKey; Util.Check(parentRefKey.ExpandedKeyMembers.Count == 1, "Composite keys are not supported in Include expressions; member: {0}", parentRefMember); var cmd = parentRefKey.SelectByKeyArrayCommand; // BookAuthor_SelectByArrayOf_BookId Util.Check(cmd != null, "Select command for entity reference {0} not defined.", parentRefKey); var linkRecs = pkValues.Length == 0 ? new List<EntityRecord>() : _session.ExecuteSelect(cmd, pkValues); //list of all IBookAuthor for Book objects in 'records' parameter var parentRefFk = parentRefKey.ExpandedKeyMembers[0].Member; var groupedLinkRecs = linkRecs.GroupBy(rec => rec.GetValueDirect(parentRefFk)); //each group is list of IBookOrder for a single book order; group key is BookOrder.Id //Target records var linkToTargetMember = listMember.ChildListInfo.OtherEntityRefMember; var linkToTargetKey = linkToTargetMember.ReferenceInfo.FromKey; var expTargetMembers = linkToTargetKey.ExpandedKeyMembers; Util.Check(expTargetMembers.Count == 1, "Include expression not supported for entities with composite keys, property: {0}.", listMember.ChildListInfo.OtherEntityRefMember); var linkToTargetFk = expTargetMembers[0].Member; var fkValues = GetMemberValues(linkRecs, linkToTargetFk); var targetKey = linkToTargetMember.ReferenceInfo.ToKey; //IAuthor.Id Util.Check(targetKey.ExpandedKeyMembers.Count == 1, "Include expression not supported for entities with composite keys, entity: {0}.", targetKey.Entity.Name); var cmdTarget = targetKey.SelectByKeyArrayCommand; Util.Check(cmdTarget != null, "Select command for entity reference {0} not defined.", linkToTargetKey); var targetRecs = fkValues.Length == 0 ? new List<EntityRecord>() : _session.ExecuteSelect(cmdTarget, fkValues); //list of all IAuthor for Book objects in 'records' parameter //fill out lists foreach (var linkGroup in groupedLinkRecs) { var pkValue = new EntityKey(pkInfo, linkGroup.Key); // BookAuthor.Book_id var parent = _session.GetRecord(pkValue); // Book var childList = parent.ValuesTransient[listMember.ValueIndex] as IPropertyBoundList; //BookOrder.Lines, list object if (childList != null && childList.IsLoaded) continue; if (childList == null) childList = parent.InitChildEntityList(listMember); var linkEntities = linkGroup.Select(r => r.EntityInstance).ToList(); var targetEntities = linkGroup.Select(r => r.GetValue(linkToTargetMember)).ToList(); // touch and collect bookAuthor.Author references; childList.Init(targetEntities, linkEntities); } // If for some parent records child lists were empty, we need set the list property to empty list, // If it remains null, it will be considered not loaded, and app will attempt to load it again on first touch var empty = new object[] { }; foreach (var parent in records) { var value = parent.ValuesTransient[listMember.ValueIndex]; if (value == null) { var list = parent.InitChildEntityList(listMember); list.Init(empty, empty); } } return linkRecs; }
// Example: records: List<IBookOrder>, listMember: bookOrder.Lines; so we load lines for each book order private IList<EntityRecord> RunIncludeForListManyToOne(IList<EntityRecord> records, EntityMemberInfo listMember) { var pkInfo = listMember.Entity.PrimaryKey; var expMembers = pkInfo.ExpandedKeyMembers; Util.Check(expMembers.Count == 1, "Include expression not supported for entities with composite keys, property: {0}.", listMember); var pkMember = expMembers[0].Member; // IBookOrder.Id var pkValues = GetMemberValues(records, pkMember); var parentRefMember = listMember.ChildListInfo.ParentRefMember; //IBookOrderLine.Order var fromKey = parentRefMember.ReferenceInfo.FromKey; Util.Check(fromKey.ExpandedKeyMembers.Count == 1, "Composite keys are not supported in Include expressions; member: {0}", parentRefMember); var cmd = fromKey.SelectByKeyArrayCommand; // BookOrderLine_SelectByArrayOf_OrderId Util.Check(cmd != null, "Select command for entity reference {0} not defined.", fromKey); var childRecs = _session.ExecuteSelect(cmd, pkValues); //list of all IBookOrderLine for BookOrder objects in 'records' parameter //setup list properties in parent records var fk = fromKey.ExpandedKeyMembers[0].Member; //IBookOrderLine.Order_Id var groupedRecs = childRecs.GroupBy(rec => rec.GetValueDirect(fk)); //each group is list of order lines for a single book order; group key is BookOrder.Id foreach (var g in groupedRecs) { var pkValue = new EntityKey(pkInfo, g.Key); // Order_Id -> BookOrder.Id var parent = _session.GetRecord(pkValue); // BookOrder var childList = parent.ValuesTransient[listMember.ValueIndex] as IPropertyBoundList; //BookOrder.Lines, list object if (childList != null && childList.IsLoaded) continue; var childEntities = g.Select(r => r.EntityInstance).ToList(); if (childList == null) childList = parent.InitChildEntityList(listMember); childList.Init(childEntities); } // If for some parent records child lists were empty, we need set the list property to empty list, // If it remains null, it will be considered not loaded, and app will attempt to load it again on first touch foreach (var parent in records) { var value = parent.ValuesTransient[listMember.ValueIndex]; if (value == null) parent.InitChildEntityList(listMember); } return childRecs; }
private IList<EntityRecord> RunIncludeForEntityRef(IList<EntityRecord> records, EntityMemberInfo refMember) { if (records.Count == 0) return _emptyList; var fkMember = refMember.ReferenceInfo.FromKey.ExpandedKeyMembers[0].Member; // r.Book_Id var fkValues = GetMemberValues(records, fkMember); var cmd = refMember.ReferenceInfo.ToKey.SelectByKeyArrayCommand; var newRecs = _session.ExecuteSelect(cmd, fkValues); if (newRecs.Count == 0) return _emptyList; // Set ref members in parent records var targetPk = refMember.ReferenceInfo.ToKey; foreach (var parentRec in records) { var fkValue = parentRec.GetValueDirect(fkMember); if (fkValue == DBNull.Value) { parentRec.SetValueDirect(refMember, DBNull.Value); } else { var pkKey = new EntityKey(targetPk, fkValue); //we lookup in session, instead of searching in results of Include query - all just loaded records are registered in session and lookup is done by key (it is fact dict lookup) var targetRec = _session.GetRecord(pkKey); parentRec.SetValueDirect(refMember, targetRec); } } return newRecs; }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { member.OldNames = StringHelper.SplitNames(this.OldNames); }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { // Check size code and lookup in tables if (!string.IsNullOrEmpty(this.SizeCode)) { var sizeTable = context.Model.App.SizeTable; //If there is size code, look it up in SizeTable; first check module-specific value, then global value for the code int size; var fullCode = Sizes.GetFullSizeCode(member.Entity.EntityType.Namespace, this.SizeCode); if (sizeTable.TryGetValue(fullCode, out size)) { //check full code with module's namespace prefix member.Size = size; return; } if (sizeTable.TryGetValue(this.SizeCode, out size)) { //check global value, non-module-specific member.Size = size; return; } } //If size is specified explicitly, use it if(this.Size > 0) { member.Size = Size; if((this.Options & SizeOptions.AutoTrim) != 0) member.Flags |= EntityMemberFlags.AutoTrim; return; } //If no Size code and no value, it is an error, lookup in module settings context.Log.Error("Property {0}.{1}: invalid Size attribute, must specify size code or value", member.Entity.Name, member.MemberName); }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { var entity = member.Entity; if (!CreatePrimaryKey(context, entity)) return; entity.PrimaryKey.KeyMembers.Add(new EntityKeyMemberInfo(member, false)); member.Flags |= EntityMemberFlags.PrimaryKey; }
void SetValue(EntityRecord record, EntityMemberInfo member, object value) { Util.Throw("Back-ref properties are readonly, cannot set value. Property: {0}.{1}", member.Entity.Name, member.MemberName); }
object GetValue(EntityRecord record, EntityMemberInfo member) { var v = record.GetValueDirect(member); if(v != null) { if(v == DBNull.Value) return null; var rec = (EntityRecord)v; return rec.EntityInstance; } //retrieve entity var targetPk = EntityKey.CreateSafe(_targetEntity.PrimaryKey, record.PrimaryKey.Values); var targetRec = record.Session.GetRecord(targetPk); if (targetRec == null) { record.SetValueDirect(member, DBNull.Value); return null; } record.SetValueDirect(member, targetRec); if(targetRec.ByRefUserPermissions == null) targetRec.ByRefUserPermissions = member.ByRefPermissions; return targetRec.EntityInstance; }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { // It is initially assigned EntityRef if(!context.Model.IsEntity(member.DataType)) { context.Log.Error("FromBackRef attribute may be used only on properties that are references to other entities. Property: {0}.{1}", member.Entity.Name, member.MemberName); return; } _member = member; _member.Kind = MemberKind.Transient; _member.Flags |= EntityMemberFlags.FromOneToOneRef; _targetEntity = context.Model.GetEntityInfo(member.DataType); Util.Check(_targetEntity != null, "Target entity not found: {0}", member.DataType); //check that PK of target entity points back to 'this' entity var targetPkMembers = _targetEntity.PrimaryKey.KeyMembers; Util.Check(targetPkMembers.Count == 1 && targetPkMembers[0].Member.DataType == _member.Entity.EntityType, "BackRef property {0}.{1}: target entity must have Primary key pointing back to entity {0}.", _member.Entity.Name, _member.MemberName); member.GetValueRef = GetValue; member.SetValueRef = SetValue; }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { member.Kind = MemberKind.Transient; member.GetValueRef = MemberValueGettersSetters.GetTransientValue; member.SetValueRef = MemberValueGettersSetters.SetTransientValue; }
//Special case for entity references, because of LINQ specifics. // In Xml Linq mapping, we associate constructed classes (not interfaces) with db tables. // (this comes from requirement that LINQ entities should be classes with default parameterless constructors, // so interfaces do not work there). // For LINQ to work properly we need to have properties (of kind EntityRef) to be of type // of generated class, not the original entity interface. private void BuildEntityRefProperty(TypeBuilder typeBuilder, EntityMemberInfo member) { //1. Create private property matching interface property and implementing it explicitly var intfType = member.Entity.EntityType; var propName = intfType.Name + "." + member.MemberName; var propBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.None, member.DataType, Type.EmptyTypes); var getterName = intfType.Name + ".get_" + member.MemberName; var getter = CreateGetter(typeBuilder, propBuilder, member.Index, getterName, false); //Associate the created getter method with the interface method var igetter = intfType.FindMethod("get_" + member.MemberName); typeBuilder.DefineMethodOverride(getter, igetter); //var canWrite = (member.Property == null || member.Property.CanWrite); //if (canWrite) { var setterName = intfType.Name + ".set_" + member.MemberName; var setter = CreateSetter(typeBuilder, propBuilder, member.Index, setterName, false); var isetter = intfType.FindMethod("set_" + member.MemberName); if (isetter != null) typeBuilder.DefineMethodOverride(setter, isetter); //associate with setter in interface //} //2. Create public property, with the same name as member name, but with type equal to the Class // constructed on target entity. This is needed for proper interpretation by LINQ. var classType = member.Entity.ClassInfo.Type; //TypeBuilder in fact, at this moment propName = member.MemberName; var targetEntityClass = member.ReferenceInfo.ToKey.Entity.ClassInfo.Type; propBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.None, targetEntityClass, Type.EmptyTypes); getter = CreateGetter(typeBuilder, propBuilder, member.Index); //if (canWrite) CreateSetter(typeBuilder, propBuilder, member.Index); CloneCustomAttributes(propBuilder, member); }
public override void Apply(AttributeContext context, Attribute attribute, EntityMemberInfo member) { member.Flags |= EntityMemberFlags.Nullable; if (member.DataType.IsValueType) { member.Flags |= EntityMemberFlags.ReplaceDefaultWithNull; member.GetValueRef = MemberValueGettersSetters.GetValueTypeReplaceNullWithDefault; member.SetValueRef = MemberValueGettersSetters.SetValueTypeReplaceDefaultWithNull; } }