I am looking for the "best practices" way. For example: If I have an AppService for tenants where a single function exists to create tenants, but in order to create a tenant I need to also create a different entity and save it to the database for an ID, how would I accomplish this without repeating code all over the place?
public class TenantAppService : ApplicationService, ITenantAppService
{
private readonly IRepository<Tenant, long> _tenantRepository;
public TenantAppService(IRepository<Tenant, long> tenantRepository)
{
_tenantRepository = tenantRepository;
}
public void CreateTenant(CreateTenantInput input)
{
Logger.Info("Creating a tenant for input: " + input);
//Create a new Address entity with given input's properties
}
}
9 Answer(s)
-
0
Hi,
You should never inject and use an application service from another application service. This is a bad bractice.
So, how you will share login in application service? First, creating a tenant is actually a business (domain) logic task. Application logic should use a domain service (say, a TenantManager class) to create a tenant. If secondary entity is a part of the tenant entity, you can create it in TenantManager. If not, you can create it in another domain service and call it from TenantManager.
I can say 3 rules:
Application logic should be simple. It gets a DTO if needed, obtains some entities from repositories, run a domain service with these entities, and finally return another DTO if needed. This is ideal, but in a real application this can be a little hard. So, we should try to accomplish it.
Every Application service method represents a single use-case of the system (Say assigning a task to a person).
An Application Service should not directly use another app service method.
There are some other rules but these are other topics.
-
0
Thank you for the quick reply Halil!
Your explanation about the application logic is very helpful and has made me realize I've been structuring my app incorrectly.. so I will have a bit of work to do to fix it.
Are there any examples or demos that demonstrate the interaction between a domain manager or service and an application service? I am trying to wrap my mind around it and cannot seem to find any mention of it in the documentation (probably because it is simple, and I am too new at this).
Thanks again.
-
0
Hi,
Only example is module-zero for now. It's domain layer for an authorization & authentication enabled application. See Tenant Manager for example: <a class="postlink" href="https://github.com/aspnetboilerplate/module-zero/blob/master/src/Abp.Zero/MultiTenancy/AbpTenantManager.cs">https://github.com/aspnetboilerplate/mo ... Manager.cs</a> There is a CreateTenant method in this class. We can use it from an application service to create a new tenant.
And this is an example application service that updates a tenant:
[AbpAuthorize(AppPermissions.Pages_Tenants_Edit)] public async Task UpdateTenant(TenantEditDto input) { var tenant = await TenantManager.GetByIdAsync(input.Id); input.MapTo(tenant); CheckErrors(await TenantManager.UpdateAsync(tenant)); }
As you see, there is no update logic here. TenantManager does it.
I will create an article or documentation for domain services in the future.
BTW, <a class="postlink" href="http://www.aspnetzero.com/">http://www.aspnetzero.com/</a> is an implementation of ABP & module-zero. It's a commerical product. You can try a demo.
-
0
Hello Halil,
I am still having some difficulty understanding how to access the Domain Service (Tenant Manager) from the App Service (Tenant App Service).
In your example, you use:
await TenantManager.GetByIdAsync(input.Id)
but I am unsure how you get a reference to TenantManager. I am guessing you use dependency injection, but I'm not exactly sure how. I have tried constructor injection, but am getting a not so descriptive error.
-
0
Hi,
Surely, you have a TenantManager class. Then you can constructor inject it. If you're using module-zero, this is an example TenantManager: <a class="postlink" href="https://github.com/aspnetboilerplate/module-zero-template/blob/master/src/AbpZeroSample.Core/Authorization/TenantManager.cs">https://github.com/aspnetboilerplate/mo ... Manager.cs</a>
I advice you to download this project (<a class="postlink" href="https://github.com/aspnetboilerplate/module-zero-template/tree/master">https://github.com/aspnetboilerplate/mo ... ree/master</a>) and try to create a TenantAppService and inject TenantManager.
-
0
How to call a AppService from AccountController.cs ?
-
0
Just constructor-inject and use it.
-
0
From Register code in AccountController, I need to access a AppService logic that is protected by AbpAuthorize. But since the user is new, Apb session is blank, new user also does not have the permission to perform certain actions. What is the right way to temporarily escalate permission? Or programatically set AbpSession?
-
0
Hi,
First of all, an application service method should represents a single use-case in ideal. You're trying to use it for 2 use cases and requirements are different.
This is not special to AbpAuthorize attribute. Think that you did not use AbpAuthorize attribute and manually checked a permission. or witten a code that should not be executed in the second case. What would you do? Easiest way: You add a boolean flag to method and write an if to check this flag... But this is not true because clients directly uses app service and will see this flag parameter and can set it tu true/false to suppress authorization.
Best solution: Extract the shared logic (that's currently in app service method) to another class. Then use this class from app serivce and controller. App service just checkc permission and call it. Controller does not check permission and call it.