public void NamePrefixSequence_Test() { var context = new DynamicDtoTypeBuildingContext(); context.CurrentPrefix.ShouldBeEmpty(); const string level1 = "1-level"; const string level2 = "2-level"; const string level3 = "3-level"; using (context.OpenNamePrefix(level1)) { context.CurrentPrefix.ShouldBe(level1); using (context.OpenNamePrefix(level2)) { context.CurrentPrefix.ShouldBe($"{level1}.{level2}"); using (context.OpenNamePrefix(level3)) { context.CurrentPrefix.ShouldBe($"{level1}.{level2}.{level3}"); } context.CurrentPrefix.ShouldBe($"{level1}.{level2}"); } context.CurrentPrefix.ShouldBe(level1); } context.CurrentPrefix.ShouldBeEmpty(); }
public async Task BuildDynamicDto_WidthDtoReferences_Test() { var entityConfigCacheMock = new Mock <IEntityConfigCache>(); const string area1PropName = nameof(Person.AreaLevel1); entityConfigCacheMock.Setup(x => x.GetEntityPropertiesAsync(It.IsAny <Type>())) .Returns(() => { var result = new EntityPropertyDtoList(); result.Add(new EntityPropertyDto { Name = area1PropName, DataType = DataTypes.EntityReference, EntityType = "Shesha.Core.Area", }); var r = result as List <EntityPropertyDto>; return(Task.FromResult(r)); }); var entityConfigStore = LocalIocManager.Resolve <IEntityConfigurationStore>(); var cacheManager = LocalIocManager.Resolve <ICacheManager>(); var builder = new DynamicDtoTypeBuilder(entityConfigCacheMock.Object, entityConfigStore, cacheManager); var baseDtoType = typeof(DynamicDto <Person, Guid>); var context = new DynamicDtoTypeBuildingContext() { ModelType = baseDtoType, UseDtoForEntityReferences = true, // AddFormFieldsProperty = false // note: false is a default value for AddFormFieldsProperty. Leave it blank to test default value }; var proxyType = await builder.BuildDtoFullProxyTypeAsync(baseDtoType, context); proxyType.Assembly.IsDynamic.ShouldBeTrue(); var properties = proxyType.GetProperties(); properties.ShouldContain(p => p.Name == area1PropName); proxyType.ShouldNotBeAssignableTo(typeof(IHasFormFieldsList)); var area1Prop = properties.FirstOrDefault(p => p.Name == area1PropName); area1Prop.ShouldNotBeNull($"{area1PropName} property is missing in the created DTO"); var area1IsDto = area1Prop.PropertyType == typeof(EntityWithDisplayNameDto <Guid>); area1IsDto.ShouldBeTrue($"{area1PropName} is not mapped as '{nameof(EntityWithDisplayNameDto<Guid>)}'"); }
public void Apply(OpenApiSchema schema, SchemaFilterContext context) { var isProxy = typeof(IDynamicDtoProxy).IsAssignableFrom(context.Type); if (!context.Type.IsDynamicDto() || isProxy) { return; } var dtoBuilder = StaticContext.IocManager.Resolve <IDynamicDtoTypeBuilder>(); var builderContext = new DynamicDtoTypeBuildingContext { ModelType = context.Type }; var dtoType = AsyncHelper.RunSync(async() => await dtoBuilder.BuildDtoFullProxyTypeAsync(builderContext.ModelType, builderContext)); // build list of properties for case-insensitive search var propNames = schema.Properties.Select(p => p.Key.ToLower()).ToList(); context.SchemaGenerator.GenerateSchema(dtoType, context.SchemaRepository); var allProperties = dtoType.GetProperties(); foreach (var property in allProperties) { if (propNames.Contains(property.Name.ToLower())) { continue; } var propertySchema = context.SchemaGenerator.GenerateSchema(property.PropertyType, context.SchemaRepository, memberInfo: property); // note: Nullable is not processed by GenerateSchema propertySchema.Nullable = property.PropertyType.IsNullableType(); schema.Properties.Add(property.Name.ToCamelCase(), propertySchema); } // add `_formFields` with comment var formFieldsProp = typeof(IHasFormFieldsList).GetProperty(nameof(IHasFormFieldsList._formFields)); if (!propNames.Contains(formFieldsProp.Name.ToLower())) { var formFieldsSchema = context.SchemaGenerator.GenerateSchema(formFieldsProp.PropertyType, context.SchemaRepository, memberInfo: formFieldsProp); schema.Properties.Add(formFieldsProp.Name, formFieldsSchema); } }
/// <summary> /// Map entity to a custom <see cref="DynamicDto{TEntity, TPrimaryKey}"/> /// </summary> /// <typeparam name="TDynamicDto">Type of dynamic DTO</typeparam> /// <typeparam name="TEntity">Type of entity</typeparam> /// <typeparam name="TPrimaryKey">Type of entity primary key</typeparam> /// <param name="entity">entity to map</param> /// <param name="settings">mapping settings</param> /// <returns></returns> protected async Task <TDynamicDto> MapToCustomDynamicDtoAsync <TDynamicDto, TEntity, TPrimaryKey>(TEntity entity, IDynamicMappingSettings settings = null) where TEntity : class, IEntity <TPrimaryKey> where TDynamicDto : class, IDynamicDto <TEntity, TPrimaryKey> { // build dto type var context = new DynamicDtoTypeBuildingContext() { ModelType = typeof(TDynamicDto), UseDtoForEntityReferences = settings?.UseDtoForEntityReferences ?? false }; var dtoType = await DtoBuilder.BuildDtoFullProxyTypeAsync(typeof(TDynamicDto), context); var dto = Activator.CreateInstance(dtoType) as TDynamicDto; // create mapper var mapper = await DynamicDtoMappingHelper.GetEntityToDtoMapperAsync(typeof(TEntity), dtoType); // map entity to DTO mapper.Map(entity, dto); // map dynamic fields await DynamicPropertyManager.MapEntityToDtoAsync <TDynamicDto, TEntity, TPrimaryKey>(entity, dto); return(dto); }
public async Task BuildFullDynamicDto_Test() { var entityConfigCacheMock = new Mock <IEntityConfigCache>(); entityConfigCacheMock.Setup(x => x.GetEntityPropertiesAsync(It.IsAny <Type>())) .Returns(() => { var result = new EntityPropertyDtoList(); result.AddString("Name", "Name..."); result.AddString("Description", "Description..."); var r = result as List <EntityPropertyDto>; return(Task.FromResult(r)); }); var entityConfigStore = LocalIocManager.Resolve <IEntityConfigurationStore>(); var cacheManager = LocalIocManager.Resolve <ICacheManager>(); var builder = new DynamicDtoTypeBuilder(entityConfigCacheMock.Object, entityConfigStore, cacheManager); var baseDtoType = typeof(DynamicDto <Person, Guid>); var context = new DynamicDtoTypeBuildingContext() { ModelType = baseDtoType, AddFormFieldsProperty = true, }; var proxyType = await builder.BuildDtoFullProxyTypeAsync(baseDtoType, context); var properties = proxyType.GetProperties(); properties.ShouldContain(p => p.Name == "Name"); properties.ShouldContain(p => p.Name == "Description"); properties.ShouldContain(p => p.Name == nameof(IHasFormFieldsList._formFields)); proxyType.ShouldNotBeAssignableTo(typeof(IHasFormFieldsList)); }
public async Task BuildDynamicDto_Test() { var entityConfigCacheMock = new Mock <IEntityConfigCache>(); const string supervisorPropName = "Supervisor"; const string supervisorFirstNamePropName = "FirstName"; const string supervisorLastNamePropName = "LastName"; entityConfigCacheMock.Setup(x => x.GetEntityPropertiesAsync(It.IsAny <Type>())) .Returns(() => { var result = new EntityPropertyDtoList(); result.AddString("Name", "Name..."); result.AddString("Description", "Description..."); var nested = new EntityPropertyDto { Name = supervisorPropName, DataType = DataTypes.Object, Properties = new List <EntityPropertyDto>(), }; nested.Properties.Add(new EntityPropertyDto { Name = supervisorFirstNamePropName, DataType = DataTypes.String }); nested.Properties.Add(new EntityPropertyDto { Name = supervisorLastNamePropName, DataType = DataTypes.String }); result.Add(nested); var r = result as List <EntityPropertyDto>; return(Task.FromResult(r)); }); var entityConfigStore = LocalIocManager.Resolve <IEntityConfigurationStore>(); var cacheManager = LocalIocManager.Resolve <ICacheManager>(); var builder = new DynamicDtoTypeBuilder(entityConfigCacheMock.Object, entityConfigStore, cacheManager); var baseDtoType = typeof(DynamicDto <Person, Guid>); var context = new DynamicDtoTypeBuildingContext() { ModelType = baseDtoType, // AddFormFieldsProperty = false // note: false is a default value for AddFormFieldsProperty. Leave it blank to test default value }; var proxyType = await builder.BuildDtoFullProxyTypeAsync(baseDtoType, context); proxyType.Assembly.IsDynamic.ShouldBeTrue(); var properties = proxyType.GetProperties(); properties.ShouldContain(p => p.Name == "Name"); properties.ShouldContain(p => p.Name == "Description"); properties.ShouldNotContain(p => p.Name == nameof(IHasFormFieldsList._formFields)); proxyType.ShouldNotBeAssignableTo(typeof(IHasFormFieldsList)); var supervisorProp = properties.FirstOrDefault(p => p.Name == supervisorPropName); supervisorProp.ShouldNotBeNull($"{supervisorPropName} property is missing in the created DTO"); // nested object should be dynamic supervisorProp.PropertyType.Assembly.IsDynamic.ShouldBeTrue(); typeof(IDynamicNestedObject).IsAssignableFrom(supervisorProp.PropertyType).ShouldBeTrue($"Dynamic nested object must implement {nameof(IDynamicNestedObject)}"); var nestedProperties = supervisorProp.PropertyType.GetProperties(); nestedProperties.ShouldContain(p => p.Name == supervisorFirstNamePropName); nestedProperties.ShouldContain(p => p.Name == supervisorLastNamePropName); }
public async Task SaveLoad_Test() { LoginAsHostAdmin(); // test data var props = new Dictionary <EntityPropertyDto, object> { { new EntityPropertyDto { Name = "UnitTestDynamicString", DataType = DataTypes.String, Source = MetadataSourceType.UserDefined }, "testString" }, { new EntityPropertyDto { Name = "UnitTestDynamicDate", DataType = DataTypes.Date, Source = MetadataSourceType.UserDefined }, DateTime.Today }, { new EntityPropertyDto { Name = "UnitTestDynamicDateTime", DataType = DataTypes.DateTime, Source = MetadataSourceType.UserDefined }, DateTime.Now }, { new EntityPropertyDto { Name = "UnitTestDynamicBoolean", DataType = DataTypes.Boolean, Source = MetadataSourceType.UserDefined }, true }, { new EntityPropertyDto { Name = "UnitTestDynamicNumber", DataType = DataTypes.Number, Source = MetadataSourceType.UserDefined }, (decimal)123.45 }, { new EntityPropertyDto { Name = "UnitTestDynamicObject", DataType = DataTypes.Object, Source = MetadataSourceType.UserDefined, Properties = new List <EntityPropertyDto>() { new EntityPropertyDto { Name = "UnitTestObjectProperty1", DataType = DataTypes.String, Source = MetadataSourceType.UserDefined }, new EntityPropertyDto { Name = "UnitTestObjectProperty2", DataType = DataTypes.String, Source = MetadataSourceType.UserDefined } } }, new { UnitTestObjectProperty1 = "testObjectProperty1", UnitTestObjectProperty2 = "testObjectProperty2" } } }; // configure DTO var entityConfigCacheMock = new Mock <IEntityConfigCache>(); entityConfigCacheMock.Setup(x => x.GetEntityPropertiesAsync(It.IsAny <Type>())) .Returns(() => { var result = new List <EntityPropertyDto>(); foreach (var prop in props) { result.Add(prop.Key); } return(Task.FromResult(result)); }); var entityConfigStore = LocalIocManager.Resolve <IEntityConfigurationStore>(); var cacheManager = LocalIocManager.Resolve <ICacheManager>(); var builder = new DynamicDtoTypeBuilder(entityConfigCacheMock.Object, entityConfigStore, cacheManager); var baseDtoType = typeof(DynamicDto <Person, Guid>); var context = new DynamicDtoTypeBuildingContext() { ModelType = baseDtoType }; var dtoType = await builder.BuildDtoFullProxyTypeAsync(baseDtoType, context); var dto = Activator.CreateInstance(dtoType) as DynamicDto <Person, Guid>; var properties = dto?.GetType().GetProperties(); var serializationManager = Resolve <ISerializationManager>(); // Update DTO properties values with test data foreach (var prop in props) { var p = properties?.FirstOrDefault(x => prop.Key.Name.ToLower() == x.Name.ToLower()); var val = prop.Value; if (prop.Key.DataType == DataTypes.Object) { val = serializationManager.DeserializeProperty(p?.PropertyType, serializationManager.SerializeProperty(prop.Key, val)); } p?.SetValue(dto, val); } using (var uow = _unitOfWorkManager.Begin()) { var nhuow = uow as NhUnitOfWork; var session = nhuow?.GetSession(); // Create temporary Entity Properties configs var entityConfigRepo = Resolve <IRepository <EntityConfig, Guid> >(); var entityPropRepo = Resolve <IRepository <EntityProperty, Guid> >(); var config = entityConfigRepo.GetAll().FirstOrDefault(x => x.TypeShortAlias == typeof(Person).GetEntityConfiguration().TypeShortAlias); foreach (var prop in props) { var propConf = new EntityProperty() { EntityConfig = config, Name = prop.Key.Name, DataType = prop.Key.DataType, Source = MetadataSourceType.UserDefined }; prop.Key.Id = entityPropRepo.Insert(propConf).Id; foreach (var childProp in prop.Key.Properties) { var childPropConf = new EntityProperty() { ParentProperty = propConf, EntityConfig = config, Name = prop.Key.Name, DataType = prop.Key.DataType, Source = MetadataSourceType.UserDefined }; childProp.Id = entityPropRepo.Insert(childPropConf).Id; } } session?.Flush(); try { var personRepo = Resolve <IRepository <Person, Guid> >(); var dynamicPropertyManager = new DynamicPropertyManager( Resolve <IRepository <EntityProperty, Guid> >(), Resolve <IRepository <EntityPropertyValue, Guid> >(), Resolve <IEntityConfigurationStore>(), Resolve <IDynamicRepository>() ) { DtoTypeBuilder = builder, SerializationManager = serializationManager }; // Create temporary entity var entity = new Person(); var id = personRepo.Insert(entity).Id; try { // Save dynamic properties to DB await dynamicPropertyManager.MapDtoToEntityAsync <DynamicDto <Person, Guid>, Person, Guid>(dto, entity); session?.Flush(); session?.Clear(); // Get entity from DB var newEntity = personRepo.Get(id); // Create new DTO and map values from entity to DTO var newDto = Activator.CreateInstance(dtoType) as DynamicDto <Person, Guid>; await dynamicPropertyManager.MapEntityToDtoAsync <DynamicDto <Person, Guid>, Person, Guid>(newEntity, newDto); // Check values. Values from the DB should be the same as from the test list var newProperties = newDto?.GetType().GetProperties(); foreach (var prop in props) { var p = newProperties?.FirstOrDefault(x => prop.Key.Name.ToLower() == x.Name.ToLower()); var dtoValue = p?.GetValue(newDto); if (prop.Key.DataType == DataTypes.Object) { var childProperties = dtoValue.GetType().GetProperties(); foreach (var childProp in prop.Key.Properties) { var childP = childProperties.FirstOrDefault(x => childProp.Name.ToLower() == x.Name.ToLower()); var dtoChildValue = childP.GetValue(dtoValue); dtoChildValue.ShouldBe(prop.Value.GetType().GetProperty(childProp.Name).GetValue(prop.Value)); } } else { dtoValue.ShouldBe(prop.Value); } } } finally { // delete temporary entity session.CreateSQLQuery($"delete from Core_Persons where id = '{id}'").ExecuteUpdate(); } } finally { // delete temporary values and properties configs foreach (var prop in props) { session.CreateSQLQuery($"delete from Frwk_EntityPropertyValues where EntityPropertyId = '{prop.Key.Id}'").ExecuteUpdate(); if (prop.Key.DataType == DataTypes.Object) { foreach (var childProp in prop.Key.Properties) { session.CreateSQLQuery($"delete from Frwk_EntityProperties where Id = '{childProp.Id}'").ExecuteUpdate(); } } session.CreateSQLQuery($"delete from Frwk_EntityProperties where Id = '{prop.Key.Id}'").ExecuteUpdate(); } } } }