/// <summary> /// 查找所有引用指定模型标识的EntityRef Member集合 /// </summary> public List <EntityRefModel> FindEntityRefModels(ulong targetEntityModelID) { var rs = new List <EntityRefModel>(); ModelNode[] ls = FindNodesByType(ModelType.Entity); for (int i = 0; i < ls.Length; i++) { EntityModel model = (EntityModel)ls[i].Model; //注意:不能排除自身引用,主要指树状结构的实体 for (int j = 0; j < model.Members.Count; j++) { if (model.Members[j].Type == EntityMemberType.EntityRef) { EntityRefModel refMember = (EntityRefModel)model.Members[j]; //注意不排除聚合引用 for (int k = 0; k < refMember.RefModelIds.Count; k++) { if (refMember.RefModelIds[k] == targetEntityModelID) { rs.Add(refMember); } } } } } return(rs); }
/// <summary> /// 增减外键引用计数值 /// </summary> internal async ValueTask AddEntityRefAsync(EntityRefModel entityRef, ApplicationModel fromApp, Entity fromEntity, int diff) { Debug.Assert(diff != 0); Debug.Assert(fromEntity.Id.RaftGroupId != 0); var targetId = fromEntity.GetEntityId(entityRef.FKMemberIds[0]); if (targetId == null || targetId.IsEmpty) { return; } ulong targetModelId = entityRef.IsAggregationRef ? fromEntity.GetUInt64(entityRef.TypeMemberId) : entityRef.RefModelIds[0]; var targetModel = await RuntimeContext.Current.GetModelAsync <EntityModel>(targetModelId); var targetAppId = IdUtil.GetAppIdFromModelId(targetModelId); var targetApp = await RuntimeContext.Current.GetApplicationModelAsync(targetAppId); //注意编码 uint fromTableId = KeyUtil.EncodeTableId(fromApp.StoreId, entityRef.Owner.TableId); var item = new RefFromItem() { TargetEntityId = targetId, FromTableId = fromTableId, FromRaftGroupId = fromEntity.Id.RaftGroupId }; lock (this) { if (refs == null) { item.Diff = diff; refs = new List <RefFromItem> { item }; } else { for (int i = 0; i < refs.Count; i++) { if (refs[i].TargetEntityId == targetId && refs[i].FromRaftGroupId == fromEntity.Id.RaftGroupId) { item.Diff = refs[i].Diff + diff; refs[i] = item; return; } } //未找到 item.Diff = diff; refs.Add(item); } } }
private static void GetEntityMemberTypeStringAndReadOnly(EntityMemberModel mm, ref string typeString, ref bool readOnly, DesignTree designTree) { switch (mm.Type) { case EntityMemberType.DataField: //判断是否是枚举 DataFieldModel dmm = mm as DataFieldModel; //if (dmm.DataType == EntityFieldType.Enum) //{ // if (string.IsNullOrEmpty(dmm.EnumModelID)) // typeString = "int"; // else // { // string[] sr = dmm.EnumModelID.Split('.'); // typeString = sr[0] + ".Enums." + sr[1]; // } //} if (dmm.DataType == EntityFieldType.EntityId) { typeString = "EntityId"; } else { typeString = dmm.DataType.GetValueType().FullName; //TODO:简化类型名称 } //系统存储分区键与sql存储的主键为只读 readOnly |= dmm.IsPartitionKey || dmm.IsPrimaryKey; if (dmm.AllowNull && (dmm.DataType != EntityFieldType.String && dmm.DataType != EntityFieldType.EntityId && dmm.DataType != EntityFieldType.Binary && typeString != "object")) { typeString += "?"; } break; case EntityMemberType.EntityRef: EntityRefModel rm = (EntityRefModel)mm; if (rm.IsAggregationRef) { typeString = TypeHelper.Type_EntityBase; } else { if (rm.RefModelIds.Count == 0) //Todo:待移除,因误删除模型引用项导致异常 { typeString = TypeHelper.Type_EntityBase; } else { var targetModelNode = designTree.FindModelNode(ModelType.Entity, rm.RefModelIds[0]); typeString = $"{targetModelNode.AppNode.Model.Name}.Entities.{targetModelNode.Model.Name}"; } } break; case EntityMemberType.EntitySet: { EntitySetModel sm = (EntitySetModel)mm; var targetModelNode = designTree.FindModelNode(ModelType.Entity, sm.RefModelId); typeString = $"EntityList<{targetModelNode.AppNode.Model.Name}.Entities.{targetModelNode.Model.Name}>"; readOnly = true; } break; case EntityMemberType.AggregationRefField: typeString = "object"; readOnly = true; break; //case EntityMemberType.Formula: //case EntityMemberType.Aggregate: //FormulaModel fmm = mm as FormulaModel; //typeString = TypeService.GetEntityFieldValueType(fmm.DataType).FullName; //readOnly = true; //break; case EntityMemberType.Tracker: throw ExceptionHelper.NotImplemented(); //GetEntityMemberTypeStringAndReadOnly( // (mm as TrackerModel).TargetMember, ref typeString, ref readOnly); //readOnly = true; //break; case EntityMemberType.AutoNumber: typeString = "string"; readOnly = true; break; //case EntityMemberType.ImageRef: //typeString = TypeHelper.Type_IImageSource; //readOnly = false; //break; default: typeString = "object"; break; } }
static TestHelper() { SysAppModel = new ApplicationModel("appbox", Consts.SYS, Consts.SYS_APP_ID); AdminPermissionModel = new PermissionModel(Consts.SYS_PERMISSION_ADMIN_ID, "Admin"); DeveloperPermissionModel = new PermissionModel(Consts.SYS_PERMISSION_DEVELOPER_ID, "Developer"); OrderStatusModel = new EnumModel(SYS_ENUM_MODEL_ID | (1 << IdUtil.MODELID_SEQ_OFFSET), "OrderStatus"); OrderStatusModel.Items.Add(new EnumModelItem("New", 0)); OrderStatusModel.Items.Add(new EnumModelItem("Paid", 1)); EmploeeModel = new EntityModel(Consts.SYS_EMPLOEE_MODEL_ID, Consts.EMPLOEE, EntityStoreType.StoreWithMvcc); var name = new DataFieldModel(EmploeeModel, Consts.NAME, EntityFieldType.String); EmploeeModel.AddSysMember(name, Consts.EMPLOEE_NAME_ID); var account = new DataFieldModel(EmploeeModel, Consts.ACCOUNT, EntityFieldType.String); account.AllowNull = true; EmploeeModel.AddSysMember(account, Consts.EMPLOEE_ACCOUNT_ID); var password = new DataFieldModel(EmploeeModel, Consts.PASSWORD, EntityFieldType.Binary); password.AllowNull = true; EmploeeModel.AddSysMember(password, Consts.EMPLOEE_PASSWORD_ID); //Add indexes var ui_account_pass = new EntityIndexModel(EmploeeModel, "UI_Account_Password", true, new FieldWithOrder[] { new FieldWithOrder(Consts.EMPLOEE_ACCOUNT_ID) }, new ushort[] { Consts.EMPLOEE_PASSWORD_ID }); EmploeeModel.SysStoreOptions.AddSysIndex(EmploeeModel, ui_account_pass, Consts.EMPLOEE_UI_ACCOUNT_ID); //测试用分区表 VehicleStateModel = new EntityModel(((ulong)Consts.SYS_APP_ID << 32) | 18, "VehicleState", EntityStoreType.StoreWithMvcc); var vid = new DataFieldModel(VehicleStateModel, "VehicleId", EntityFieldType.Int32); VehicleStateModel.AddMember(vid); var lng = new DataFieldModel(VehicleStateModel, "Lng", EntityFieldType.Float); VehicleStateModel.AddMember(lng); var lat = new DataFieldModel(VehicleStateModel, "Lat", EntityFieldType.Float); VehicleStateModel.AddMember(lat); var pks = new PartitionKey[1]; pks[0] = new PartitionKey() { MemberId = vid.MemberId, OrderByDesc = false }; VehicleStateModel.SysStoreOptions.SetPartitionKeys(VehicleStateModel, pks); //测试树状结构 ulong orgUnitModelId = ((ulong)Consts.SYS_APP_ID << 32) | 19; OrgUnitModel = new EntityModel(orgUnitModelId, "OrgUnit", EntityStoreType.StoreWithMvcc); var ouName = new DataFieldModel(OrgUnitModel, "Name", EntityFieldType.String); OrgUnitModel.AddSysMember(ouName, 1 << IdUtil.MEMBERID_SEQ_OFFSET); var parentId = new DataFieldModel(OrgUnitModel, "ParentId", EntityFieldType.EntityId, true); parentId.AllowNull = true; OrgUnitModel.AddSysMember(parentId, 2 << IdUtil.MEMBERID_SEQ_OFFSET); var parent = new EntityRefModel(OrgUnitModel, "Parent", orgUnitModelId, new ushort[] { parentId.MemberId }); parent.AllowNull = true; OrgUnitModel.AddSysMember(parent, 3 << IdUtil.MEMBERID_SEQ_OFFSET); var childs = new EntitySetModel(OrgUnitModel, "Childs", orgUnitModelId, parent.MemberId); OrgUnitModel.AddSysMember(childs, 4 << IdUtil.MEMBERID_SEQ_OFFSET); //----以下测试映射至SqlStore的实体--- SqlStoreModel = new DataStoreModel(DataStoreKind.Sql, "appbox.Store.PostgreSQL;appbox.Store.PgSqlStore", "DemoDB"); ulong cityModelId = ((ulong)Consts.SYS_APP_ID << 32) | 25; CityModel = new EntityModel(cityModelId, "City", new SqlStoreOptions(SqlStoreModel.Id)); var cityCode = new DataFieldModel(CityModel, "Code", EntityFieldType.Int32); CityModel.AddMember(cityCode); var cityName = new DataFieldModel(CityModel, "Name", EntityFieldType.String); CityModel.AddMember(cityName); var cityPerson = new DataFieldModel(CityModel, "Persons", EntityFieldType.Int32); CityModel.AddMember(cityPerson); var cityPk = new List <FieldWithOrder>(); cityPk.Add(new FieldWithOrder { MemberId = cityCode.MemberId, OrderByDesc = false }); CityModel.SqlStoreOptions.SetPrimaryKeys(CityModel, cityPk); ulong customerModelId = ((ulong)Consts.SYS_APP_ID << 32) | 26; CustomerModel = new EntityModel(customerModelId, "Customer", new SqlStoreOptions(SqlStoreModel.Id)); var customerId = new DataFieldModel(CustomerModel, "Id", EntityFieldType.Int32); CustomerModel.AddMember(customerId); var customerName = new DataFieldModel(CustomerModel, "Name", EntityFieldType.String); CustomerModel.AddMember(customerName); var customerCityId = new DataFieldModel(CustomerModel, "CityId", EntityFieldType.Int32, true); CustomerModel.AddMember(customerCityId); var customerCity = new EntityRefModel(CustomerModel, "City", cityModelId, new ushort[] { customerCityId.MemberId }); CustomerModel.AddMember(customerCity); var customerPk = new List <FieldWithOrder>(); customerPk.Add(new FieldWithOrder { MemberId = customerId.MemberId, OrderByDesc = false }); CustomerModel.SqlStoreOptions.SetPrimaryKeys(CustomerModel, customerPk); ulong orderModelId = ((ulong)Consts.SYS_APP_ID << 32) | 27; OrderModel = new EntityModel(orderModelId, "Order", new SqlStoreOptions(SqlStoreModel.Id)); var orderId = new DataFieldModel(OrderModel, "Id", EntityFieldType.Int32); OrderModel.AddMember(orderId); var orderCustomerId = new DataFieldModel(OrderModel, "CustomerId", EntityFieldType.Int32, true); OrderModel.AddMember(orderCustomerId); var orderCustomer = new EntityRefModel(OrderModel, "Customer", customerModelId, new ushort[] { orderCustomerId.MemberId }); OrderModel.AddMember(orderCustomer); var orderPk = new List <FieldWithOrder>(); orderPk.Add(new FieldWithOrder { MemberId = orderId.MemberId, OrderByDesc = false }); OrderModel.SqlStoreOptions.SetPrimaryKeys(OrderModel, orderPk); }
/// <summary> /// 返回树状结构的实体集合 /// </summary> /// <param name="childrenMember">例:q.T["SubItems"]</param> /// <returns></returns> public async Task <EntityList> ToTreeListAsync(MemberExpression childrenMember) { //TODO:目前实现仅支持单一主键且为Guid的树状结构 Debug.Assert(ReferenceEquals(childrenMember.Owner, T)); var children = (EntitySetExpression)childrenMember; EntityModel model = await RuntimeContext.Current.GetModelAsync <EntityModel>(T.ModelID); EntitySetModel childrenModel = (EntitySetModel)model.GetMember(children.Name, true); EntityRefModel parentModel = (EntityRefModel)model.GetMember(childrenModel.RefMemberId, true); DataFieldModel parentIdModel = (DataFieldModel)model.GetMember(parentModel.FKMemberIds[0], true); TreeParentIDMember = (FieldExpression)T[parentIdModel.Name]; var pk = model.SqlStoreOptions.PrimaryKeys[0].MemberId; AddAllSelects(this, model, T, null); //TODO:加入自动排序 //if (!string.IsNullOrEmpty(setmodel.RefRowNumberMemberName)) //{ // SqlSortItem sort = new SqlSortItem(T[setmodel.RefRowNumberMemberName], SortType.ASC); // SortItems.Insert(0, sort); //} //如果没有设置任何条件,则设置默认条件为查询根级开始 if (Equals(null, Filter)) { Filter = TreeParentIDMember == null; } Purpose = QueryPurpose.ToEntityTreeList; EntityList list = new EntityList(childrenModel); var db = SqlStore.Get(model.SqlStoreOptions.StoreModelId); var dic = new Dictionary <Guid, Entity>(); //TODO: fix pk using var cmd = db.BuildQuery(this); using var conn = db.MakeConnection(); await conn.OpenAsync(); cmd.Connection = conn; Log.Debug(cmd.CommandText); using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { Entity obj = FillEntity(model, reader); //设置obj本身的EntitySet成员为已加载,防止从数据库中再次加载 obj.InitEntitySetForLoad(childrenModel); var parentId = obj.GetGuidNullable(parentIdModel.MemberId); if (parentId.HasValue && dic.TryGetValue(parentId.Value, out Entity parent)) { parent.GetEntitySet(childrenModel.MemberId).Add(obj); } else { list.Add(obj); } dic.Add(obj.GetGuid(pk), obj); } return(list); }
/// <summary> /// 加载全表为树形结构 /// </summary> public async ValueTask <EntityList> ToTreeListAsync(ushort setMemberId) //TODO:入参排序表达式 { filter = null; //TODO:暂忽略过滤器 var model = await RuntimeContext.Current.GetModelAsync <EntityModel>(modelId); if (model == null) { throw new Exception($"EntityModel[{modelId}] not exists."); } if (!(model.GetMember(setMemberId, true) is EntitySetModel setModel)) { throw new ArgumentException("Must assign EntitySet member id", nameof(setMemberId)); } if (setModel.RefModelId != model.Id) { throw new ArgumentException("Can't be a tree"); } EntityRefModel refModel = (EntityRefModel)setModel.Owner.GetMember(setModel.RefMemberId, true); var list = await ToListAsync(); if (list == null) { return(null); } //TODO:暂简单实现,待优化为排序后处理 //var sortedList = list.OrderBy(t => t.GetEntityId(setModel.RefMemberId) ?? Guid.Empty); //TODO:check order //Dictionary<Guid, Entity> dic = new Dictionary<Guid, Entity>(); var res = new EntityList(setModel); foreach (var obj in list /*sortedList*/) { //根据上级标识依次加入 var parentId = obj.GetEntityId(refModel.FKMemberIds[0]); if (parentId == null) { res.Add(obj); } else { var parent = list.SingleOrDefault(t => (Guid)t.Id == (Guid)parentId); if (parent != null) { parent.InitEntitySetForLoad(setModel); //先尝初始化EntitySet为已加载状态 parent.GetEntitySet(setMemberId).Add(obj); } else { res.Add(obj); } //if (dic.TryGetValue(parentId, out Entity parent)) // parent.GetEntitySet(setMemberId).Add(obj); //else //res.Add(obj); } //dic.Add(obj.Id, obj); } return(res); }
protected override IList <DbCommand> MakeAlterTable(EntityModel model, Design.IDesignContext ctx) { //TODO:***处理主键变更 var tableName = model.GetSqlTableName(false, ctx); StringBuilder sb; bool needCommand = false; //用于判断是否需要处理NpgsqlCommand List <string> fks = new List <string>(); //引用外键列表 List <DbCommand> commands = new List <DbCommand>(); //List<DbCommand> funcCmds = new List<DbCommand>(); //先处理表名称有没有变更,后续全部使用新名称 if (model.IsNameChanged) { var oldTableName = model.GetSqlTableName(true, ctx); var renameTableCmd = new NpgsqlCommand($"ALTER TABLE \"{oldTableName}\" RENAME TO \"{tableName}\""); commands.Add(renameTableCmd); } //处理删除的成员 var deletedMembers = model.Members.Where(t => t.PersistentState == PersistentState.Deleted).ToArray(); if (deletedMembers != null && deletedMembers.Length > 0) { #region ----删除的成员---- sb = StringBuilderCache.Acquire(); foreach (var m in deletedMembers) { if (m.Type == EntityMemberType.DataField) { needCommand = true; sb.AppendFormat("ALTER TABLE \"{0}\" DROP COLUMN \"{1}\";", tableName, ((DataFieldModel)m).SqlColOriginalName); } else if (m.Type == EntityMemberType.EntityRef) { EntityRefModel rm = (EntityRefModel)m; if (!rm.IsAggregationRef) { var fkName = $"FK_{rm.Owner.Id}_{rm.MemberId}"; //TODO:特殊处理DbFirst导入表的外键约束名称 fks.Add($"ALTER TABLE \"{tableName}\" DROP CONSTRAINT \"{fkName}\";"); } } } var cmdText = StringBuilderCache.GetStringAndRelease(sb); if (needCommand) { //加入删除的外键SQL for (int i = 0; i < fks.Count; i++) { sb.Insert(0, fks[i]); sb.AppendLine(); } commands.Add(new NpgsqlCommand(cmdText)); } #endregion } //reset needCommand = false; fks.Clear(); //处理新增的成员 var addedMembers = model.Members.Where(t => t.PersistentState == PersistentState.Detached).ToArray(); if (addedMembers != null && addedMembers.Length > 0) { #region ----新增的成员---- sb = StringBuilderCache.Acquire(); foreach (var m in addedMembers) { if (m.Type == EntityMemberType.DataField) { needCommand = true; sb.AppendFormat("ALTER TABLE \"{0}\" ADD COLUMN ", tableName); BuildFieldDefine((DataFieldModel)m, sb, false); sb.Append(";"); } else if (m.Type == EntityMemberType.EntityRef) { var rm = (EntityRefModel)m; if (!rm.IsAggregationRef) //只有非聚合引合创建外键 { fks.Add(BuildForeignKey(rm, ctx, tableName)); //考虑CreateGetTreeNodeChildsDbFuncCommand } } } var cmdText = StringBuilderCache.GetStringAndRelease(sb); if (needCommand) { //加入关系 sb.AppendLine(); for (int i = 0; i < fks.Count; i++) { sb.AppendLine(fks[i]); } commands.Add(new NpgsqlCommand(cmdText)); } #endregion } //reset needCommand = false; fks.Clear(); //处理修改的成员 var changedMembers = model.Members.Where(t => t.PersistentState == PersistentState.Modified).ToArray(); if (changedMembers != null && changedMembers.Length > 0) { #region ----修改的成员---- foreach (var m in changedMembers) { if (m.Type == EntityMemberType.DataField) { DataFieldModel dfm = (DataFieldModel)m; //先处理数据类型变更,变更类型或者变更AllowNull或者变更默认值 if (dfm.IsDataTypeChanged) { sb = StringBuilderCache.Acquire(); sb.AppendFormat("ALTER TABLE \"{0}\" ALTER COLUMN ", tableName); string defaultValue = BuildFieldDefine(dfm, sb, true); if (dfm.AllowNull) { sb.AppendFormat(",ALTER COLUMN \"{0}\" DROP NOT NULL", dfm.SqlColOriginalName); } else { if (dfm.DataType == EntityFieldType.Binary) { throw new Exception("Binary field must be allow null"); } sb.AppendFormat(",ALTER COLUMN \"{0}\" SET NOT NULL,ALTER COLUMN \"{0}\" SET DEFAULT {1}", dfm.SqlColOriginalName, defaultValue); } commands.Add(new NpgsqlCommand(StringBuilderCache.GetStringAndRelease(sb))); } //再处理重命名列 if (m.IsNameChanged) { var renameColCmd = new NpgsqlCommand($"ALTER TABLE \"{tableName}\" RENAME COLUMN \"{dfm.SqlColOriginalName}\" TO \"{dfm.SqlColName}\""); commands.Add(renameColCmd); } } //TODO:处理EntityRef更新与删除规则 //注意不再需要同旧实现一样变更EntityRef的外键约束名称 "ALTER TABLE \"XXX\" RENAME CONSTRAINT \"XXX\" TO \"XXX\"" //因为ModelFirst的外键名称为FK_{MemberId};CodeFirst为导入的名称 } #endregion } //处理索引变更 BuildIndexes(model, commands, tableName); return(commands); }