Service call to service at Aspnet Zero Boilerplate - c#

I have a couple of simple services that exchange information:
public class Service2: PPlusAppServiceBase
{
private readonly IAbpSession _session;
public Service2(IAbpSession session)
{
_session = session;
}
public Entity getEntity()
{
Entity et = new Entity();
Service1 _service1 = new Service1(_session);
[...]
_service1.getEntity();
[...]
return et;
}
}
public class Service1: PPlusAppServiceBase
{
private readonly IAbpSession _session;
public Service1(IAbpSession session)
{
_session = session;
}
public Entity getEntity()
{
_session.[...]
return et;
}
}
OK, it works properly and I just have to send the session to work. But how can I do it simply when I have to get information from a more complex service? Like the default Boilerplate? For example, EditionAppService:
public class EditionAppService : PPlusAppServiceBase, IEditionAppService
{
private readonly EditionManager _editionManager;
private readonly IRepository<SubscribableEdition> _editionRepository;
private readonly IRepository<Tenant> _tenantRepository;
private readonly IBackgroundJobManager _backgroundJobManager;
public EditionAppService(
EditionManager editionManager,
IRepository<SubscribableEdition> editionRepository,
IRepository<Tenant> tenantRepository,
IBackgroundJobManager backgroundJobManager)
{
_editionManager = editionManager;
_editionRepository = editionRepository;
_tenantRepository = tenantRepository;
_backgroundJobManager = backgroundJobManager;
}
[AbpAuthorize(AppPermissions.Pages_Editions)]
public async Task<ListResultDto<EditionListDto>> GetEditions()
{
var editions = await (from edition in _editionRepository.GetAll()
join expiringEdition in _editionRepository.GetAll() on edition.ExpiringEditionId equals expiringEdition.Id into expiringEditionJoined
from expiringEdition in expiringEditionJoined.DefaultIfEmpty()
select new
{
Edition = edition,
expiringEditionDisplayName = expiringEdition.DisplayName
}).ToListAsync();
var result = new List<EditionListDto>();
foreach (var edition in editions)
{
var resultEdition = ObjectMapper.Map<EditionListDto>(edition.Edition);
resultEdition.ExpiringEditionDisplayName = edition.expiringEditionDisplayName;
result.Add(resultEdition);
}
return new ListResultDto<EditionListDto>(result);
}
}
As you can see, the constructor is more complex, the constructor data comes directly defined by swagger (ASP.NET Boilerplate creates dynamic drivers and swagger, and it is these that carry this data that they use as a builder), but when making the call from another service I can't get them.
What is the best way to do that is edit the minimum the second?
In Service2, where I have to call EditionAppService.GetEditions I need something like:
EditionAppService _editionAppService = new EditionAppService();
_editionAppService.GetEditions().Result;
But wait for the builder I don't have

That design pattern is called Dependency Injection.
Do this instead:
public class Service2: PPlusAppServiceBase
{
private readonly EditionAppService _editionAppService; // Add this
private readonly Service1 _service1; // Add this
private readonly IAbpSession _session;
public Service2(
EditionAppService editionAppService, // Add this
Service1 service1, // Add this
IAbpSession session)
{
_editionAppService = editionAppService; // Add this
_service1 = service1; // Add this
_session = session;
}
public Entity getEntity()
{
Entity et = new Entity();
// Service1 _service1 = new Service1(_session); // Remove this
// ...
_service1.getEntity();
// ...
return et;
}
// ...
}
Related: Should I be calling an AppService from another AppService?

Related

Circular reference in dependency injection

I am creating a RESTful api in Net 5, according to the instructions I must create repositories and services that make use of them. The logic must be in the services.
The Services I have are:
SubGroupService
GroupsService
The problem I have is that I generated a circular reference since in GroupsService I need a method of SubGroupsService and SubGroupsService i need a method of GroupsService .
Injecting the GroupsService service into SubGroupsService there is no problem, but injecting SubGroupsService into GroupsService generates the circular reference.
Please can you tell me how to solve this type of problem, since I don't have much experience with dependency injection.
SubGroupService
public class SubGroupService: ISubGroupService
{
private readonly ISubGroupRepository _SubGroupRepository;
private readonly IGroupService _GroupService;
public SubGroupService(
ISubGroupRepository SubGroupRepository,
IGroupService GroupService
{
_SubGroupRepository = SubGroupRepository;
_GroupService = GroupService;
}
public async Task InsertSubGroupService(Subgroup subgroup)
{
var group = await _GroupService.GetGroupService(subgroup.idgroup);
if (group != null)
{
await _SubGroupRepository.InsertSubGroupRepository(subgroup);
}
else
{
throw new BusinessException("This group not exists");
}
}
public async Task<Subgroups> GetSubGroupService(int idgroup)
{
return await _SubGroupRepository.GetSubGroupRepository(idgroup);
}
}
Group Service
public class GroupService : IGroupService
{
private readonly ISubGroupService _SubGroupService;
private readonly IGroupRepository _GroupRepository;
public GroupService(
ISubGroupService SubGroupService,
IGroupRepository GroupRepository)
{
_SubGroupService = SubGroupService;
_GroupRepository = GroupRepository;
}
public async Task<bool> DeleteGroupService(int Idgroup)
{
var existsSubGroup = await _SubGroupRepository(Idgroup);
if(existsSubGroup == null)
{
return await _GroupRepository.DeleteGroupRepository(Idgroup);
}
}
public async Task<Groups> GetGroupService(int Idgroup)
{
return await _GroupRepository.GetGroupRepository(Idgroup);
}
}
Interfaces:
public interface IGroupService
{
Task<Groups> GetGroupsService(int Idgroup);
Task<bool> DeleteGroupService(int Idgroup);
}
public interface ISubGroupService
{
Task<Subgroups> GetSubGroupService(int idsubgrupo);
Task InsertSubgroupService(Subgroup subgroup);
}
You can't use constructor injection in that case. You can switch to property injection:
public class SubGroupService: ISubGroupService
{
private readonly ISubGroupRepository _SubGroupRepository;
public IGroupService GroupService { get; set; }
public SubGroupService(
ISubGroupRepository SubGroupRepository)
{
_SubGroupRepository = SubGroupRepository;
}
// methods of the class
}
public class GroupService : IGroupService
{
public ISubGroupService SubGroupService {get; set;}
private readonly IGroupRepository _GroupRepository;
public GroupService(
IGroupRepository GroupRepository)
{
_GroupRepository = GroupRepository;
}
// methods of the class
}
You'll have to create the objects like this:
IGroupRepository groupRepository = new GroupRepository();
IGroupService groupService = new GroupService(groupRepository);
ISubGroupService subGroupService = new SubGroupService(groupRepository);
groupService.SubGroupSerivce = subGroupService;
subGroupService.GroupService = groupService;
Of course, creation of the objects is now much more complicated. You might put the creation into a facotry method to avoid errors:
public (IGroupService,ISubGroupService) CreateGroupAndSubGroupService()
{
// code from above
}
And it is also advisable to add null checks, because someone might create the objects without initializing the service correctly.

C# Wrapping and initializing multiple into an interface

I have a api controller as below:
public class ValuesController : Controller
{
private static string dynamoDbTable = string.Empty;
private IDynamoDbClientInitialization _clientAccessor;
public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbClientInitialization clientAccessor)
{
var vals = appSettings.Value;
dynamoDbTable = vals["dynamoDbTable"];
_clientAccessor = clientAccessor;
}
[HttpGet("data")]
public async Task<List<MyData>> GetData()
{
List<ScanCondition> conditions = new List<ScanCondition>();
var response = await _clientAccessor.GetContext().ScanAsync<MyData>(conditions, AWSHelperMethods.GetDynamoDbOperationConfig(dynamoDbTable)).GetRemainingAsync();
return response.ToList();
}
}
Also I have my helper class as:
public static class AWSHelperMethods
{
public static BasicAWSCredentials SetAwsCredentials(string awsId, string awsPassword)
{
var creds = new BasicAWSCredentials(awsId, awsPassword);
return creds;
}
public static AmazonDynamoDBClient GetDynamoDbClient(BasicAWSCredentials creds, RegionEndpoint awsDynamoDbRegion)
{
var client = new AmazonDynamoDBClient(creds, awsDynamoDbRegion);
return client;
}
public static DynamoDBContext GetDynamoDbContext(AmazonDynamoDBClient client)
{
var context = new DynamoDBContext(client);
return context;
}
public static DynamoDBOperationConfig GetDynamoDbOperationConfig(string dynamoDbTable)
{
DynamoDBOperationConfig config = new DynamoDBOperationConfig() { OverrideTableName = dynamoDbTable };
return config;
}
}
My IDynamoDbClientInitialization is as:
public interface IDynamoDbClientInitialization
{
DynamoDBContext GetContext();
}
public class DynamoDbClientInitialization : IDynamoDbClientInitialization
{
private readonly DynamoDbClientSettings settings;
private DynamoDBContext _awsContext;
public DynamoDbClientInitialization(IOptions<DynamoDbClientSettings> options)
{
settings = options?.Value;
}
public DynamoDBContext GetContext()
{
//Check is context already exists. If not create a new one.
if(_awsContext != null)
{
return _awsContext;
}
else
{
var creds = AWSHelperMethods.SetAwsCredentials(settings.Id, settings.Password);
var dynamoClient = AWSHelperMethods.GetDynamoDbClient(creds, settings.Region);
_awsContext = AWSHelperMethods.GetDynamoDbContext(dynamoClient);
return _awsContext;
}
}
}
And finally my startup is as:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDynamoDbClientInitialization, DynamoDbClientInitialization>();
services.Configure<DynamoDbClientSettings>(c =>
{
c.Id = Configuration.GetValue<string>("AppSettings:awsId");
c.Password = Configuration.GetValue<string>("AppSettings:awsPassword");
c.Region = RegionEndpoint.GetBySystemName(Configuration.GetValue<string>("AppSettings:dynamoDbRegion"));
});
}
Now I created the above code without taking into consideration of Interfaces. Now I need to write unit test cases for this so wanted to know how can I create a single class and wrap up all the above
code instead of creating multiple classes like DynamoDbClientInitialization and Helper class. So that instead of initializing each and every class I just initialize a single class say "DbManager" that handles everything above.
You could use an IoC Container. (See comparison here), IoC Containers can resolve needed services automatically. Let's assume that we have interfaces IA, IB, IC and classes A, B, C implementing them:
public class A : IA
{
...
}
public class B : IB
{
private readonly IA _a;
public B(IA a)
{
_a = a;
}
...
}
public class C : IC
{
private readonly IB _b;
public C(IB b)
{
_b = b;
}
...
}
Then you would initialize the container with (the details differ between different implementations, but this gives you an idea):
public static ISomeContainer Container { get; } = container = new SomeContainer();
...
container.Register<IA, A>();
container.Register<IB, B>();
container.Register<IC, C>();
You can also specify that you want services to be created as singletons. E.g.
container.Register<IB, B>().AsSingleton();
Now you can get a service with
var myC = container.Resolve<IC>();
Now the container automatically creates an A object, then creates a B object using the first object as constructor argument. Finally, it creates and returns a C object using the B object as constructor argument.
This means that you can have different classes implementing the same interface, but having different types and numbers of constructor parameters, and the IoC resolves all these parameters automatically.
You can also register different classes for the same interface using a key that you can specify when resolving a service.

Autofac SingleInstance WebApi Performance

We've improved the performance of our API's adding services without state to .SingleInstance() , but I've a question, regarding the demo code attached,
The IBusAppService that we are using on the controller is set to SingleInstance(), but inside the BusAppService, we are using more Interfaces, for example ( IBusRepository or IBusDomainService )
So the question is, in order to increase the performnace, should we set all interfaces to SingleInstance() inside the IBusAppService or the performance is the same because they are inside a SingleInstance??
I'll attach here some code with the workflow :
The ApiController:
public class BusApiController : ApiController
{
private readonly IBusAppService _iBusAppService;
private readonly IBusMapper _iBusMapper;
public BusApiController(IBusAppService iBusAppService,
IBusMapper iBusMapper)
{
_iBusAppService = iBusAppService;
_iBusMapper = iBusMapper;
}
[HttpGet]
public BusResponse Get(long id)
{
var bus = _iBusAppService.Get(id);
var busResponse = _iBusMapper.Convert(bus);
return busResponse;
}
}
public class BusResponse {
public long Id { get; set; }
}
public interface IBusMapper
{
BusResponse Convert(Bus bus);
}
public class BusMapper : IBusMapper
{
public BusResponse Convert(Bus bus)
{
if (bus == null) return null;
var result = new BusResponse{Id = bus.Id};
return result;
}
}
builder.RegisterType<BusAppService>().As<IBusAppService>().SingleInstance();
builder.RegisterType<BusMapper>().As<IBusMapper>().SingleInstance();
The ApplicationService
public interface IBusAppService
{
Bus Get(long id);
}
public class BusAppService : IBusAppService
{
private readonly IBusRepository _iBusRepository;
private readonly IBusDomainService _iBusDomainService;
public BusAppService(IBusRepository iBusRepository, IBusDomainService iBusDomainService )
{
_iBusRepository = iBusRepository;
_iBusDomainService = iBusDomainService;
}
public Bus Get(long id)
{
var bus = this._iBusRepository.Get(id);
var busTax = this._iBusDomainService.CalculateTax(bus);
var result = bus;
return result;
}
}
Anything consumed by a single instance service will end up being single instance due to captive dependencies. You could change them to be single instance, too, but it won't necessarily change the performance related to instantiation cost that you see now.

Changing database at runtime with MVC WebApi 2

I want to change the connection to a database at runtime in a REST Api. I want to put a variable of the request and let the Api decide which connectionstring to use.
For example:
I put the variable "dbid" with the value "develop" in the request header and send it to the Api.
The Api sees the header and gets the correct connectionstring from the web.config.
I have three layers (data, business, api). The data contains EntityFramework to get and set data. Like this:
public class WebsiteContext : IocDbContext, IWebsites
{
public DbSet<Website> Websites { get; set; }
public IEnumerable<Website> GetAll()
{
return Websites.ToList();
}
}
(IoCDbContext.cs)
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext() : base("develop")
{
}
public void ChangeDatabase(string connectionString)
{
Database.Connection.ConnectionString= connectionString;
}
}
In the business I have a class to retrieve data from the datalayer and do some logical stuff (not needed here, but still good for the story).
public class Websites : IWebsites
{
private readonly Data.Interfaces.IWebsites _websiteContext;
#region Constructor
public Websites(Data.Interfaces.IWebsites websiteContext)
{
_websiteContext = websiteContext;
}
#endregion
#region IWebsites implementation
public IEnumerable<Website> GetWebsites()
{
List<Data.Objects.Website> websiteDtos = _websiteContext.GetAll().ToList();
return websiteDtos.Select(web => web.ToModel()).ToList();
}
#endregion
}
public static class WebsiteMapper
{
public static Website ToModel(this Data.Objects.Website value)
{
if (value == null)
return null;
return new Website
{
Id = value.Id,
Name = value.Name
};
}
}
And, last but not least, the controller:
public class WebsiteController : ApiController
{
private readonly IWebsites _websites;
public WebsiteController(IWebsites websites)
{
_websites = websites;
}
public IEnumerable<Website> GetAll()
{
return _websites.GetWebsites().ToList();
}
}
My Unity configuration:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<Business.Interfaces.IWebsites, Websites>();
container.RegisterType<IDbContext, IocDbContext>();
container.RegisterType<IWebsites, WebsiteContext>();
// e.g. container.RegisterType<ITestService, TestService>();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
So as you can see the connection string with the name "develop" is used by default. This will return a website with the name "website". Now I would change the header variable "dbid" to "live". The api should see this and should get the connectionstring that corresponds with the name "live". This last part is something I am trying, but nothing works.
This I tried:
Adding session to webapi. This means I break the stateless idea of REST api: not done
Statics cannot work either, because everyone could get the same connectionstring, but its user specific
Google, but most of the examples don't work for me
Searching StackOverflow... See previous point.
This is driving me crazy! There should be a way to change the connectionstring given by a value in a request header, right?
I have the same scenario in a multi-tenant application I created where I use a different connection string for each tenant.
It doesn't matter the implementation you choose, but you have to determine how you are going to differentiate each request per connection string. In my application, I created a custom route value, and used it in the url to differentiate each request. The important thing is to create whatever this mechanism is, and it needs to be the 1st thing you register in your DI framework, on a per request basis.
For example (using Ninject):
private static void RegisterServicdes(IKernel kernel)
{
kernel.Bind<ISiteContext>().To<SiteContext>().InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
// register other services...
}
Rather than your implementation of your DbContext, I would change to be this, then always create your DbContext instance via a DbContextFactory.
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext(string connectionStringType) : base(connectionStringType) { }
}
Then you need to create a DbContextFactory that you use when you create your DbContext, and take the above class as a dependency. Or you can take the dependency into your services, and pass it into the DbContextFactory instead.
public interface IDbContextFactory
{
TestModel CreateContext();
}
public class DbContextFactory : IDbContextFactory
{
private string _siteType;
public DbContextFactory(ISiteContext siteContext)
{
_siteType = siteContext.Tenant;
}
public TestModel CreateContext()
{
return new TestModel(FormatConnectionStringBySiteType(_siteType));
}
// or you can use this if you pass the IMultiTenantHelper dependency into your service
public static TestModel CreateContext(string siteName)
{
return new TestModel(FormatConnectionStringBySiteType(siteName));
}
private static string FormatConnectionStringBySiteType(string siteType)
{
// format from web.config
string newConnectionString = #"data source={0};initial catalog={1};integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
if (siteType.Equals("a"))
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbOne");
}
else
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbTwo");
}
}
}
Then you can use it like so when accessing your DbContext:
public class DbAccess
{
private IDbContextFactory _dbContextFactory;
public DbAccess(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public void DoWork()
{
using (IocDbContext db = _dbContextFactory.CreateContext())
{
// use EF here...
}
}
}
ISiteContext interface implementation (for using route).
public interface ISiteContext
{
string Tenant { get; }
}
public class SiteContext : ISiteContext
{
private const string _routeId = "tenantId";
private string _tenant;
public string Tenant { get { return _tenant; } }
public SiteContext()
{
_tenant = GetTenantViaRoute();
}
private string GetTenantViaRoute()
{
var routedata = HttpContext.Current.Request.RequestContext.RouteData;
// Default Routing
if (routedata.Values[_routeId] != null)
{
return routedata.Values[_routeId].ToString().ToLower();
}
// Attribute Routing
if (routedata.Values.ContainsKey("MS_SubRoutes"))
{
var msSubRoutes = routedata.Values["MS_SubRoutes"] as IEnumerable<IHttpRouteData>;
if (msSubRoutes != null && msSubRoutes.Any())
{
var subRoute = msSubRoutes.FirstOrDefault();
if (subRoute != null && subRoute.Values.ContainsKey(_routeId))
{
return (string)subRoute.Values
.Where(x => x.Key.Equals(_routeId))
.Select(x => x.Value)
.Single();
}
}
}
return string.Empty;
}
}
API action:
[Route("api/{tenantId}/Values/Get")]
[HttpGet]
public IEnumerable<string> Get()
{
_testService.DoDatabaseWork();
return new string[] { "value1", "value2" };
}
you need to create a factory class for Dynamic picking of connection string.
It is the responsibility of that class to give correct connectionString based on the certain Parameter.

Shared services in Repository Pattern

I am wondering how to deal with a situation when inside one service lets say ICompanyService I need to call another method from IUserAccountService. ?
So generally lets say that a Company shouldn't exist without an UserAccount.
The IUserAccount implementation service class looks like this:
public class UserAccountService : CrudService<UserAccount>, IUserAccountService
{
private readonly IRepository<UserAccount> _userAccountRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public CompanyService(IRepository<UserAccount> userAccountRepository,
IUnitOfWorkFactory unitOfWorkFactory)
: base(userAccountRepository, unitOfWorkFactory)
{
_userAccRepository = userAccRepository;
}
public int RegisterUser(UserAccount user) {
using (var uow=_unitOfWorkFactory.Create())
{
// Details omitted for brievity
var userId = _userAccountRepository.Create(user);
uow.Commit();
return userId;
}
}
//Other service methods
}
The company ICompanyService implementation:
public class CompanyService : CrudService<Company>, ICompanyService
{
private readonly IRepository<Company> _companyRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public CompanyService(IRepository<Company> companyRepository,
IUnitOfWorkFactory unitOfWorkFactory)
: base(companyRepository, unitOfWorkFactory)
{
_companyRepository= companyRepository;
}
public int CreateCompanyWithUserAccount(Company company) {
using (var uow=_unitOfWorkFactory.Create())
{
// Some validation with the company.Details omitted for brievity
// Here I need an instance of IUserAccountService
// Suppose I get it through DI or IoC
var userAccountService = IoC.Resolve<IUserAccountService>();
### // Is such approach good or bad?! ###
var userId = userAccountService.RegisterUser(company.UserAccount);
// Map the user id to the company
company.UserAccount.Id = userId;
var companyId = _companyRepository.Create(company);
uow.Commit();
return companyId;
}
}
//Other service methods
}
ORM under the repository is: NHibernate
Seems you have wrong constructor in UserAccountService implementations: public CompanyService
In CompanyService implementation, you better resolve IUserAccountService dependency right in a constructor, so you do it once per object creation, not each time you call method.
There's no problems with those dependencies. If two objects of IUnitOfWorkFactory implementations are problem -> make a singleton
You could just take a dependency on the IRepository<UserAccount>:
public class CompanyService : CrudService<Company>, ICompanyService
{
private readonly IRepository<Company> _companyRepository;
private readonly IRepository<UserAccount> _userAccountRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public CompanyService(IRepository<Company> companyRepository,
IUnitOfWorkFactory unitOfWorkFactory
IRepository<UserAccount> userAccountRepository)
: base(companyRepository, unitOfWorkFactory)
{
_companyRepository= companyRepository;
_userAccountRepository = userAccountRepository;
}
public int CreateCompanyWithUserAccount(Company company) {
using (var uow=_unitOfWorkFactory.Create())
{
// Some validation with the company.Details omitted for brievity
var userId = _userAccountRepository.Create(company.UserAccount);
// Map the user id to the company
company.UserAccount.Id = userId;
var companyId = _companyRepository.Create(company);
uow.Commit();
return companyId;
}
}
//Other service methods
}
IMO, it's better to take a dependency on the repository. After all your company service is creating a company and it needs to do some work in the database, which is what the repositories are for. From what I can see in the code, there's no need to involve the UserAccountService.

Categories

Resources