Base solution for your next web application

Activities of "gpcaretti"

I am newbie of ASP.NET BoilerPlate (ABP) and I am trying to understand how to create custom mappings using AutoMapper.

if I have two classes like the following where I want the property AB to be automapped as a join of A and B:

[AutoMapTo(typeof(DestClass)]  // <= do I need it for this case?
public class SourceClass {
    public string A { get; set; }
    public string B { get; set; }
}

public class DestClass {
    public string AB { get; set; }
}

I image I have to init automapper properly by using this kind of code:

Mapper.CreateMap<SourceClass, DestClass>()
    .ForMember(dest => dest.AB,
        opts => opts.MapFrom(src => (src.A + ", " + src.B)));

My question is: As I am in my web module and the classes are used by my AppService, where do I place the automapper init code above? Also, do I need to decorate the classes with the [AutoMap] attrib (I think, no)?

Thank you

I try to reply by myself hoping some feedback about my solution.

  1. In MyProject.Application project I injected my Automapper custom maps by registering my mapperConfiguration. Note that I directly used the Castle IOC register methods because I did not find any useful registering method for objects in ABP.
[DependsOn(typeof(MyProjectCoreModule), typeof(AbpAutoMapperModule))]
    public class MyProjectApplicationModule : AbpModule
    {
        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());

            // --- MY CODE
            var mapperConfiguration = new MapperConfiguration(cfg => {
                cfg.AddProfile(new MyProjectMapperProfile());  // <= here my custom mapping
            });

            var mapper = mapperConfiguration.CreateMapper();
            IocManager.IocContainer.Register(
                    Castle.MicroKernel.Registration.Component.For<IMapper>().Instance(mapper)
                );
            // --- MY CODE
        }
    }
  1. I used my mapper as injection in my Application Service:
public class UserAppService : MyNewHouseAppServiceBase, IUserAppService {
        . . .

        public UserAppService(IRepository<User, long> userRepository, AutoMapper.IMapper mapper) {
            . . .
        }


        public async Task<ListResultOutput<UserListDto>> GetUsers() {
                var users = await _userRepository.GetAllListAsync();

                return new ListResultOutput<UserListDto>(
                               // USE THE INJECTED MAPPER
                                _mapper.Map<List<UserListDto>>(users)
                            );
        }
   }

What do you think about this solution?

Tnx gp

Answer

This is the same issue I have with tenant data.

All my tests running on non-tenant data works, but when I call the AppService working on tenant data it always return 0.

I tried to disable filters, set the correct tenant Id without success.

This is the test

public async Task GetShowCase_Test() {
            // I call the DB just to be sure the query returns the correct # of items (16)
            IList<PropertyForSale> dbShowCase = null;
            await UsingDbContextAsync(async context => {
                dbShowCase = await context.REProperties.OfType<PropertyForSale>()
                                        .Include(p => p.SaleAgreement)
                                        .Where(p => p.SaleAgreement == null)
                                        .ToListAsync();
            });

            var input = new GetPfsShowCaseInput {
                MaxResultCount = 100,
                SkipCount = 0
            };

            var output = await _pfsAppService.GetShowCase(input);
            output.Items.Count.ShouldBeGreaterThan(0);  // this fails as items are 0!
        }

This is the service method that works when I run it normally

public async Task<GetPfsShowCaseOutput> GetShowCase(GetPfsShowCaseInput input) {
                // this returns 0 tiems during unit test
                var items = _propertiesManager.GetShowCase(input.SkipCount, input.MaxResultCount, input.Sorting);
                return new GetPfsShowCaseOutput {
                    Items = items.MapTo<ReadOnlyCollection<PropertyForSaleDto>>()
                };
            }
        }

And this is the Repository code:

public IQueryable<PropertyForSale> GetShowCase(int skipCount, int maxResultCount, string sorting = null) {
            var query = GetAll()
                .Where(p => (p.ShowOnline && (p.SaleAgreement == null)));

            // sorting and paging
            query = query
                .OrderByDescending(p => p.ShowOnlineDate);
            query = query
                .Skip(skipCount)
                .Take(maxResultCount);
            return query;
        }
Answer

Well,

  1. I set the TenantId to 1 and seeding in MyAppTestBase:
//Seed initial data for default tenant
            AbpSession.TenantId = 1;
            UsingDbContext(context =>
            {
                new TenantRoleAndUserBuilder(context, AbpSession.GetTenantId()).Create();
                new DefaultTenantBusinessDataCreator(context, AbpSession.GetTenantId()).Create(20); // Gp - create app business data               
            });

It is you original code. My code is just the last line (// Gp ...)

  1. REProperties entity implements IMustHaveTenant

TenantId is correctly 1, when I extract it from DB:

PropertyForSale pfs = null;
            await UsingDbContextAsync(async context => {
                pfs = await context.REProperties.OfType<PropertyForSale>()
                                .Include(p => p.Owner)
                                .Include(p => p.Solicitor)
                                .Include(p => p.ZipCode)
                                .Include(p => p.City)
                                .Include(p => p.Attachments)
                                .Include(p => p.SaleAgreement)
                                .FirstAsync();
            });

But when I execute the line

_myRepository.FirstOrDefaultAsync(p => p.Id == pfsId)

or

AbpSession.TenantId = 1;
         . . .
       _myRepository.FirstOrDefaultAsync(p => p.Id == pfsId)

it returns nothing.

even the code _myRepository.GetAllList() returns an empty list.

  1. Yes I did :-(
Answer

Thanks again for your reply. I went on with tests and I think it might be a bug somewhere (in your code?) as I found a workaround to solve the issue:

if I ran the same test by calling first a _userAppService.FindUser(...), everything works! See my working new testing code:

[Fact]
public async Task GetPropertyFull_Test() {
    PropertyForSale entity = null;
    await UsingDbContextAsync(AbpSession.TenantId, async context => {
        entity = await context.REProperties.OfType<PropertyForSale>()
            .FirstAsync(u => (u.TenantId == AbpSession.TenantId));
    });
    // only needed for a possible bug!
    await _userAppService.FindUser(long.MaxValue); // only needed for a possible bug!

    // test
    var output = await _pfsAppService.GetPropertyFull(entity.Id);
    output.ShouldNotBeNull();
    output.Id.ShouldBe(entity.Id);
}

This test works! But if I comment out code lines related to user, specifically the line:

var userDto = await _userAppService.FindUser(long.MaxValue); // only needed for a possible bug!

the test fails.

The test works even if I move the 'finduser' code in the Test's constructor leaving my test method clean:

public PfsAppService_Tests() {
    _pfsAppService = Resolve<IPfsAppService>();

    // only needed for a possible bug!
    _userAppService = Resolve<IUserAppService>();
    _userAppService.FindUser(long.MaxValue);
}

[Fact]
public async Task GetPropertyFull_Test() {
    PropertyForSale entity = null;
    await UsingDbContextAsync(AbpSession.TenantId, async context => {
        entity = await context.REProperties.OfType<PropertyForSale>()
            .FirstAsync(u => (u.TenantId == AbpSession.TenantId));
    });

    // test
    var output = await _pfsAppService.GetPropertyFull(entity.Id);
    output.ShouldNotBeNull();
    output.Id.ShouldBe(entity.Id);
}

What I miss?

Answer

Going further to this point, I got the exception received on my unit test.

When I run this code:

[Fact]
public async Task GetPropertyFull_Test() {
    PropertyForSale entity = null;
    await UsingDbContextAsync(AbpSession.TenantId, async context => {
        entity = await context.REProperties.OfType<PropertyForSale>()
            .FirstAsync(u => (u.TenantId == AbpSession.TenantId));
    });
   . . .
}

The exception I receive when fetching the entity is:

System.TypeInitializationException : The type initializer for 'Effort.DbConnectionFactory' threw an exception.
---- Effort.Exceptions.EffortException : The Effort library failed to register its provider automatically, so manual registration is required.

a) Call the Effort.Provider.EffortProviderConfiguration.RegisterProvider() method at entry point of the application

or

b) Add the following configuration to the App.config file:
   <system.data>
      <DbProviderFactories>
         <add name="Effort.Provider"
              invariant="Effort.Provider"
              description="Effort.Provider"
              type="Effort.Provider.EffortProviderFactory, Effort" />
      </DbProviderFactories>
   </system.data>

   <entityFramework>
      <providers>
         <provider invariantName="Effort.Provider"
                   type="Effort.Provider.EffortProviderServices, Effort" />
      </providers>
   </entityFramework>

But If I add this line of code (theoretically not neede) at the beginning of the code, all works:

[Fact]
public async Task GetPropertyFull_Test() {
     var dummyService = Resolve<IUserAppService>();  // why?

    PropertyForSale entity = null;
    await UsingDbContextAsync(AbpSession.TenantId, async context => {
    ...
}

Anybody can help me?

Tnx, gp

Answer

<cite>hikalkan: </cite> this maybe related to that: <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1163">https://github.com/aspnetboilerplate/as ... ssues/1163</a> After v0.10 release, please try it again.

It is still there, but I went on with my investigation and I was able to get the exception thrown when running the code:

await UsingDbContextAsync(AbpSession.TenantId, async context => {
        entity = await context.REProperties.OfType<PropertyForSale>()
            .FirstAsync(u => (u.TenantId == AbpSession.TenantId));
    });

I receive this error:

Result StackTrace:	
at Effort.DbConnectionFactory.CreateTransient()
   at MyNewHouse.Tests.MyNewHouseTestBase.UseSingleDatabase() in C:\WA\GpES\MyNewHouse\Tests\MyNewHouse.Tests\MyNewHouseTestBase.cs:line 68
   at MyNewHouse.Tests.MyNewHouseTestBase.PreInitialize() in C:\WA\GpES\MyNewHouse\Tests\MyNewHouse.Tests\MyNewHouseTestBase.cs:line 60
   at Abp.TestBase.AbpIntegratedTestBase`1.InitializeAbp() in D:\Halil\GitHub\aspnetboilerplate\src\Abp.TestBase\TestBase\AbpIntegratedTestBase.cs:line 42
   at Abp.TestBase.AbpIntegratedTestBase`1..ctor(Boolean initializeAbp) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.TestBase\TestBase\AbpIntegratedTestBase.cs:line 34
   at Abp.TestBase.AbpIntegratedTestBase`1..ctor(Boolean initializeAbp) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.TestBase\TestBase\AbpIntegratedTestBase.cs:line 34
   at MyNewHouse.Tests.MyNewHouseTestBase..ctor() in C:\WA\GpES\MyNewHouse\Tests\MyNewHouse.Tests\MyNewHouseTestBase.cs:line 27
   at MyNewHouse.Tests.Locations.CityAppService_Tests..ctor() in C:\WA\GpES\MyNewHouse\Tests\MyNewHouse.Tests\Locations\CityAppService_Tests.cs:line 24
   at MyNewHouse.Tests.Auctions.AuctionAppService_Tests..ctor() in C:\WA\GpES\MyNewHouse\Tests\MyNewHouse.Tests\Auctions\AuctionAppService_Tests.cs:line 22
----- Inner Stack Trace -----
   at Effort.Provider.EffortProviderConfiguration.RegisterDbConfigurationEventHandler()
   at Effort.Provider.EffortProviderConfiguration.RegisterProvider()
   at Effort.DbConnectionFactory..cctor()
----- Inner Stack Trace -----
   at System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationManager.AddLoadedHandler(EventHandler`1 handler)
   at System.Data.Entity.DbConfiguration.add_Loaded(EventHandler`1 value)
   at Effort.Provider.EffortProviderConfiguration.RegisterDbConfigurationEventHandler()
Result Message:	
System.TypeInitializationException : The type initializer for 'Effort.DbConnectionFactory' threw an exception.
---- Effort.Exceptions.EffortException : The Effort library failed to register its provider automatically, so manual registration is required.

a) Call the Effort.Provider.EffortProviderConfiguration.RegisterProvider() method at entry point of the application

or

b) Add the following configuration to the App.config file:
   &lt;system.data&gt;
      &lt;DbProviderFactories&gt;
         &lt;add name=&quot;Effort.Provider&quot;
              invariant=&quot;Effort.Provider&quot;
              description=&quot;Effort.Provider&quot;
              type=&quot;Effort.Provider.EffortProviderFactory, Effort&quot; /&gt;
      &lt;/DbProviderFactories&gt;
   &lt;/system.data&gt;


   &lt;entityFramework&gt;
      &lt;providers&gt;
         &lt;provider invariantName=&quot;Effort.Provider&quot;
                   type=&quot;Effort.Provider.EffortProviderServices, Effort&quot; /&gt;
      &lt;/providers&gt;
   &lt;/entityFramework&gt;
-------- System.InvalidOperationException : The Entity Framework was already using a DbConfiguration instance before an attempt was made to add an 'Loaded' event handler. 'Loaded' event handlers can only be added as part of application start up before the Entity Framework is used. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.

I hope I helped you. Gp

<cite>Shyamjith: </cite> I did wrote code here but i can't invoke it using Repository

What do you mean with this sentence? Do you mean you don't see methods from a client class of your repository? If so, it is a matter of interfaces.

Your concrete repository is probably declare in this way:

public class MySpecificRepo : SampleRepositoryBase<MyEntity, int>, IMySpecificRepo {
   ...
}

This means, clients see your class by the interface IMySpecificRepo.

So you have to define a ISampleRepositoryBase interface declaring the new methods added to SampleRepositoryBase and declare your Concrete Interface by extending it:

public interface ISampleRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> {
   // here your additional common methods declarations
  ...
}

 public interface IMySpecificRepo :  ISampleRepositoryBase<MyEntity, int> {
   // here your specific repository methods
   ...
}

public class MySpecificRepo : SampleRepositoryBase<MyEntity, int>, IMySpecificRepo {
   // here the implementations
   ...
}

In this way all works

Gp

Answer

Hi All, I have the same problem. Anyone solved the issue?

Hi, I am trying to implement a method into a service that create a user and then assign a role to it.

The problem is that to assign the role, I need the User.Id of the created user and I have not it but any FindBy into the same transaction return null due to the fact (I think) I am into the unit of work:

public class UserAppService : MyNewHouseAppServiceBase, IUserAppService {
  // ....
 public async Task CreateAdmin(CreateUserInput input) {
        var user = input.MapTo<User>();
       (await UserManager.CreateAsync(user)).CheckErrors(LocalizationManager);

       var u = await UserManager.FindByNameAsync(input.UserName); // <-- this returns null!!!
       (await UserManager.AddToRoleAsync(u.Id, StaticRoleNames.Tenants.Admin)).CheckErrors(LocalizationManager);
   }
}

I can I get the user.Id by staying into the unit of work? I wish to have the operation all transaction.

Tnx gp

Showing 1 to 10 of 29 entries