pull/1710/head
Alper Ebicoglu 5 years ago
commit 6c54e1acd3

@ -146,7 +146,7 @@ public interface IBookAppService : IApplicationService
`BookDto` is a simple [DTO](Data-Transfer-Objects.md) class defined as below:
````csharp
[AutoMapFrom(typeof(Book))] //Defines the mapping
[AbpAutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto
{
public Guid Id { get; set; }
@ -159,7 +159,7 @@ public class BookDto
}
````
* `BookDto` defines `[AutoMapFrom(typeof(Book))]` attribute to create the object mapping from `Book` to `BookDto`.
* `BookDto` defines `[AbpAutoMapFrom(typeof(Book))]` attribute to create the object mapping from `Book` to `BookDto`.
Then you can implement the `GetAsync` method as shown below:
@ -248,7 +248,7 @@ public interface IAsyncCrudAppService<
DTO classes used in this example are `BookDto` and `CreateUpdateBookDto`:
````csharp
[AutoMapFrom(typeof(Book))]
[AbpAutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -258,7 +258,7 @@ public class BookDto : AuditedEntityDto<Guid>
public float Price { get; set; }
}
[AutoMapTo(typeof(Book))]
[AbpAutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]

@ -0,0 +1,61 @@
# Dapper Integration
Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface.
Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository<TDbContext>` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper.
These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md).
## Installation
Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer):
```shell
Install-Package Volo.Abp.Dapper
```
Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md):
````C#
using Volo.Abp.Dapper;
using Volo.Abp.Modularity;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpDapperModule))]
public class MyModule : AbpModule
{
//...
}
}
````
## Implement Dapper Repository
The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods.
`DbConnection` and `DbTransaction` are from the `DapperRepository` base class.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency
{
public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetAllPersonNames()
{
return (await DbConnection.QueryAsync<string>("select Name from People", transaction: DbTransaction))
.ToList();
}
public virtual async Task<int> UpdatePersonNames(string name)
{
return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name },
DbTransaction);
}
}
```

@ -256,6 +256,10 @@
{
"text": "MongoDB Integration",
"path": "MongoDB.md"
},
{
"text": "Dapper Integration",
"path": "Dapper.md"
}
]
},

@ -146,7 +146,7 @@ public interface IBookAppService : IApplicationService
`BookDto`是一个简单的[DTO](Data-Transfer-Objects.md)类, 定义如下:
````csharp
[AutoMapFrom(typeof(Book))] //Defines the mapping
[AbpAutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto
{
public Guid Id { get; set; }
@ -159,7 +159,7 @@ public class BookDto
}
````
* `BookDto`定义了`[AutoMapFrom(typeof(Book))]`属性来从创建对象映射Book到BookDto.
* `BookDto`定义了`[AbpAutoMapFrom(typeof(Book))]`属性来从创建对象映射Book到BookDto.
然后你可以实现`GetAsync`方法. 如下所示:
@ -247,7 +247,7 @@ public interface IAsyncCrudAppService<
示例中使用的DTO类是`BookDto`和`CreateUpdateBookDto`:
````csharp
[AutoMapFrom(typeof(Book))]
[AbpAutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -257,7 +257,7 @@ public class BookDto : AuditedEntityDto<Guid>
public float Price { get; set; }
}
[AutoMapTo(typeof(Book))]
[AbpAutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]

@ -0,0 +1,61 @@
# Dapper 集成
由于Dapper的思想是sql语句优先, 且主要为`IDbConnection`接口提供了一些扩展方法.
Abp并没有为Dapper封装太多功能. Abp Dapper在Abp EntityFrameworkCore的基础上提供了`DapperRepository<TDbContext>`基类, 在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性.
这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合.
## 安装
请先根据[EF Core的集成文档](Entity-Framework-Core.md)安装并配置好EF Core.
`Volo.Abp.Dapper`是Dapper集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层):
```shell
Install-Package Volo.Abp.Dapper
```
然后添加 `AbpDapperModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
````C#
using Volo.Abp.Dapper;
using Volo.Abp.Modularity;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpDapperModule))]
public class MyModule : AbpModule
{
//...
}
}
````
## 实现Dapper仓储
下面的代码实现了`Person`仓储, 它需要EF Core的`DbContext`(MyAppDbContext). 你可以注入`PersonDapperRepository`来调用它的方法.
`DbConnection`和`DbTransaction`来自于`DapperRepository`基类.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency
{
public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetAllPersonNames()
{
return (await DbConnection.QueryAsync<string>("select Name from People", transaction: DbTransaction))
.ToList();
}
public virtual async Task<int> UpdatePersonNames(string name)
{
return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name },
DbTransaction);
}
}
```

@ -10,7 +10,7 @@
Install-Package Volo.Abp.EntityFrameworkCore
```
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.md)(项目中的Mudole类):
````C#
using Volo.Abp.EntityFrameworkCore;

@ -242,6 +242,10 @@
{
"text": "MongoDB 集成",
"path": "MongoDB.md"
},
{
"text": "Dapper 集成",
"path": "Dapper.md"
}
]
},

@ -27,7 +27,16 @@
{
<li class="nav-item">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@menuItem.DisplayName</a>
<a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@if (menuItem.Icon != null)
{
if (menuItem.Icon.StartsWith("fa"))
{
<i class="@menuItem.Icon"></i>
}
}
@menuItem.DisplayName
</a>
<div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)">
@foreach (var childMenuItem in menuItem.Items)
{

@ -10,6 +10,13 @@
if (Model.Url != null)
{
<a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)>
@if (Model.Icon != null)
{
if (Model.Icon.StartsWith("fa"))
{
<i class="@Model.Icon"></i>
}
}
@Model.DisplayName
</a>
}

@ -1,13 +1,9 @@
using System;
using System.Linq;
using System.Reflection;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Reflection;
namespace Volo.Abp.AutoMapper
{
@ -34,7 +30,6 @@ namespace Volo.Abp.AutoMapper
void ConfigureAll(IAbpAutoMapperConfigurationContext ctx)
{
FindAndAutoMapTypes(ctx);
foreach (var configurator in options.Configurators)
{
configurator(ctx);
@ -59,36 +54,5 @@ namespace Volo.Abp.AutoMapper
scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = mapperConfiguration.CreateMapper();
}
}
private void FindAndAutoMapTypes(IAbpAutoMapperConfigurationContext context)
{
//TODO: AutoMapping (by attributes) can be optionally enabled/disabled.
var typeFinder = context.ServiceProvider.GetRequiredService<ITypeFinder>();
var logger = context.ServiceProvider.GetRequiredService<ILogger<AbpAutoMapperModule>>();
var types = typeFinder.Types.Where(type =>
{
var typeInfo = type.GetTypeInfo();
return typeInfo.IsDefined(typeof(AutoMapAttribute)) ||
typeInfo.IsDefined(typeof(AutoMapFromAttribute)) ||
typeInfo.IsDefined(typeof(AutoMapToAttribute));
}
).ToArray();
if (types.Length <= 0)
{
logger.LogDebug($"No class found with auto mapping attributes.");
}
else
{
logger.LogDebug($"Found {types.Length} classes define auto mapping attributes.");
foreach (var type in types)
{
logger.LogDebug(type.FullName);
context.MapperConfiguration.CreateAutoAttributeMaps(type);
}
}
}
}
}

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using Volo.Abp.Collections;
@ -17,8 +18,30 @@ namespace Volo.Abp.AutoMapper
ValidatingProfiles = new TypeList<Profile>();
}
public void AddMaps<TModule>(bool validate = false)
{
var assembly = typeof(TModule).Assembly;
Configurators.Add(context =>
{
context.MapperConfiguration.AddMaps(assembly);
});
if (validate)
{
var profileTypes = assembly
.DefinedTypes
.Where(type => typeof(Profile).IsAssignableFrom(type) && !type.IsAbstract && !type.IsGenericType);
foreach (var profileType in profileTypes)
{
ValidatingProfiles.Add(profileType);
}
}
}
public void AddProfile<TProfile>(bool validate = false)
where TProfile: Profile, new()
where TProfile : Profile, new()
{
Configurators.Add(context =>
{
@ -27,7 +50,25 @@ namespace Volo.Abp.AutoMapper
if (validate)
{
ValidatingProfiles.Add<TProfile>();
ValidateProfile(typeof(TProfile));
}
}
public void ValidateProfile<TProfile>(bool validate = true)
where TProfile : Profile
{
ValidateProfile(typeof(TProfile), validate);
}
public void ValidateProfile(Type profileType, bool validate = true)
{
if (validate)
{
ValidatingProfiles.AddIfNotContains(profileType);
}
else
{
ValidatingProfiles.Remove(profileType);
}
}
}

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapAttribute : AutoMapAttributeBase
{
public AutoMapAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(type, targetType, MemberList.Source);
configuration.CreateMap(targetType, type, MemberList.Destination);
}
}
}
}

@ -1,17 +0,0 @@
using System;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public abstract class AutoMapAttributeBase : Attribute
{
public Type[] TargetTypes { get; }
protected AutoMapAttributeBase(params Type[] targetTypes)
{
TargetTypes = targetTypes;
}
public abstract void CreateMap(IMapperConfigurationExpression configuration, Type type);
}
}

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapFromAttribute : AutoMapAttributeBase
{
public MemberList MemberList { get; set; } = MemberList.Destination;
public AutoMapFromAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public AutoMapFromAttribute(MemberList memberList, params Type[] targetTypes)
: this(targetTypes)
{
MemberList = memberList;
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(targetType, type, MemberList);
}
}
}
}

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapToAttribute : AutoMapAttributeBase
{
public MemberList MemberList { get; set; } = MemberList.Source;
public AutoMapToAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public AutoMapToAttribute(MemberList memberList, params Type[] targetTypes)
: this(targetTypes)
{
MemberList = memberList;
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(type, targetType, MemberList);
}
}
}
}

@ -1,17 +0,0 @@
using System;
using System.Reflection;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
internal static class AutoMapperConfigurationExtensions
{
public static void CreateAutoAttributeMaps(this IMapperConfigurationExpression configuration, Type type)
{
foreach (var autoMapAttribute in type.GetTypeInfo().GetCustomAttributes<AutoMapAttributeBase>())
{
autoMapAttribute.CreateMap(configuration, type);
}
}
}
}

@ -8,7 +8,10 @@ namespace Volo.Abp.AutoMapper
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<AutoMapperTestModule>();
});
}
}
}

@ -30,8 +30,8 @@ namespace Volo.Abp.AutoMapper
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<ValidatedProfile>(true);
options.AddProfile<NonValidatedProfile>();
options.AddMaps<TestModule>(validate: true); //Adds all profiles in the TestModule assembly by validating configurations
options.ValidateProfile<NonValidatedProfile>(validate: false); //Exclude a profile from the configuration validation
});
}
}

@ -1,75 +0,0 @@
using AutoMapper;
using Shouldly;
using Xunit;
namespace Volo.Abp.AutoMapper
{
public class AutoMapper_Inheritance_Tests
{
private readonly IMapper _mapper;
public AutoMapper_Inheritance_Tests()
{
var config = new MapperConfiguration(configuration =>
{
configuration.CreateAutoAttributeMaps(typeof(MyTargetClassToMap));
configuration.CreateAutoAttributeMaps(typeof(EntityDto));
configuration.CreateAutoAttributeMaps(typeof(DerivedEntityDto));
});
_mapper = config.CreateMapper();
}
[Fact]
public void Should_Map_Derived_To_Target()
{
var derived = new MyDerivedClass { Value = "fortytwo" };
var target = _mapper.Map<MyTargetClassToMap>(derived);
target.Value.ShouldBe("fortytwo");
}
public class MyBaseClass
{
public string Value { get; set; }
}
public class MyDerivedClass : MyBaseClass
{
}
[AutoMapFrom(typeof(MyBaseClass))]
public class MyTargetClassToMap
{
public string Value { get; set; }
}
//[Fact] //TODO: That's a problem but related to AutoMapper rather than ABP.
public void Should_Map_EntityProxy_To_EntityDto_And_To_DrivedEntityDto()
{
var proxy = new EntityProxy() { Value = "42"};
var target = _mapper.Map<EntityDto>(proxy);
var target2 = _mapper.Map<DerivedEntityDto>(proxy);
target.Value.ShouldBe("42");
target2.Value.ShouldBe("42");
}
private class Entity
{
public string Value { get; set; }
}
private class DerivedEntity : Entity { }
private class EntityProxy : DerivedEntity { }
[AutoMapFrom(typeof(Entity))]
private class EntityDto
{
public string Value { get; set; }
}
[AutoMapFrom(typeof(DerivedEntity))]
private class DerivedEntityDto : EntityDto { }
}
}

@ -1,157 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
using Shouldly;
using Xunit;
namespace Volo.Abp.AutoMapper
{
public class AutoMapping_Tests
{
private readonly IMapper _mapper;
public AutoMapping_Tests()
{
var config = new MapperConfiguration(configuration =>
{
configuration.CreateAutoAttributeMaps(typeof(MyClass1));
configuration.CreateAutoAttributeMaps(typeof(MyClass2));
});
_mapper = config.CreateMapper();
}
[Fact]
public void Map_Null_Tests()
{
MyClass1 obj1 = null;
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.ShouldBe(null);
}
[Fact]
public void Map_Null_Existing_Object_Tests()
{
MyClass1 obj1 = null;
var obj2 = new MyClass2 { TestProp = "before map" };
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe("before map");
}
[Fact]
public void MapTo_Tests()
{
var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.TestProp.ShouldBe("Test value");
var obj3 = _mapper.Map<MyClass3>(obj1);
obj3.TestProp.ShouldBe("Test value");
}
[Fact]
public void MapTo_Existing_Object_Tests()
{
var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = new MyClass2();
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe("Test value");
var obj3 = new MyClass3();
_mapper.Map(obj2, obj3);
obj3.TestProp.ShouldBe("Test value");
Assert.ThrowsAny<Exception>(() => //Did not define reverse mapping!
{
_mapper.Map(obj3, obj2);
});
}
[Fact]
public void MapFrom_Tests()
{
var obj2 = new MyClass2 { TestProp = "Test value" };
var obj1 = _mapper.Map<MyClass1>(obj2);
obj1.TestProp.ShouldBe("Test value");
}
[Fact]
public void IgnoreMap_Tests()
{
var obj2 = new MyClass2 {TestProp = "Test value", AnotherValue = 42};
var obj3 = _mapper.Map<MyClass3>(obj2);
obj3.TestProp.ShouldBe("Test value");
obj3.AnotherValue.ShouldBe(0); //Ignored because of IgnoreMap attribute!
}
[Fact]
public void MapTo_Collection_Tests()
{
var list1 = new List<MyClass1>
{
new MyClass1 {TestProp = "Test value 1"},
new MyClass1 {TestProp = "Test value 2"}
};
var list2 = _mapper.Map<List<MyClass2>>(list1);
list2.Count.ShouldBe(2);
list2[0].TestProp.ShouldBe("Test value 1");
list2[1].TestProp.ShouldBe("Test value 2");
}
[Fact]
public void Map_Should_Set_Null_Existing_Object_Tests()
{
MyClass1 obj1 = new MyClass1 { TestProp = null };
var obj2 = new MyClass2 { TestProp = "before map" };
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe(null);
}
[Fact]
public void Should_Map_Nullable_Value_To_Null_If_It_Is_Null_On_Source()
{
var obj1 = new MyClass1();
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.NullableValue.ShouldBeNull();
}
[Fact]
public void Should_Map_Nullable_Value_To__Not_Null_If_It_Is__Not_Null_On_Source()
{
var obj1 = new MyClass1 { NullableValue = 42 };
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.NullableValue.ShouldBe(42);
}
[AutoMap(typeof(MyClass2), typeof(MyClass3))]
private class MyClass1
{
public string TestProp { get; set; }
public long? NullableValue { get; set; }
}
[AutoMapTo(typeof(MyClass3))]
private class MyClass2
{
public string TestProp { get; set; }
public long? NullableValue { get; set; }
public int AnotherValue { get; set; }
}
private class MyClass3
{
public string TestProp { get; set; }
[IgnoreMap]
public int AnotherValue { get; set; }
}
}
}

@ -2,7 +2,6 @@
namespace Volo.Abp.AutoMapper.SampleClasses
{
[AutoMap(typeof(MyEntity))]
public class MyEntityDto
{
public Guid Id { get; set; }

@ -0,0 +1,12 @@
using AutoMapper;
namespace Volo.Abp.AutoMapper.SampleClasses
{
public class MyMapProfile : Profile
{
public MyMapProfile()
{
CreateMap<MyEntity, MyEntityDto>().ReverseMap();
}
}
}

@ -1,12 +1,10 @@
using System;
using System.Collections.ObjectModel;
using Volo.Abp.AutoMapper;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.TestApp.Domain
{
[AutoMapTo(typeof(PersonEto))]
public class Person : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; set; }

@ -0,0 +1,12 @@
using AutoMapper;
namespace Volo.Abp.TestApp.Domain
{
public class TestAutoMapProfile : Profile
{
public TestAutoMapProfile()
{
CreateMap<PersonEto, Person>().ReverseMap();
}
}
}

@ -37,6 +37,8 @@ namespace Volo.Abp.TestApp
ctx.MapperConfiguration.CreateMap<Person, PersonDto>().ReverseMap();
ctx.MapperConfiguration.CreateMap<Phone, PhoneDto>().ReverseMap();
});
options.AddMaps<TestAppModule>();
});
}

Loading…
Cancel
Save