I'm not sure if this is possible, or possible but not recommended, or just a bit of a smell.
I have a class Customer which contains a lot of data about a customer record, pulled from different sources, or calculated.
To support this, I have a service injected CustomerServices, which a controller can Inject and create a Customer object from the class.
What I would like to do is, something like:
Pseudo-service.cs
public class CustomerServices
{
public Customer GetCustomer(string customerId)
{
var customer = new Customer();
// Do stuff from data source and build customer object
return customer; // <-- new instance of customer object
}
public List<Contract> GetCustomerContracts(Customer customer)
{
var contracts = new List<Contract>();
// Do stuff to build contract things - requires DI into this service class
return contracts; // <-- Or return the customer object
}
}
Pseudo-controller.cs
private readyonly CustomerServices _customerServices;
public PseudoController(CustomerServices customerServices)
{
_customerServices = customerServices; // DI here
}
public IActionResult GetCustomerAndContracts(string customerId)
{
// _customerServices is injected in the constructor
var customer = _customerServices.GetCustomer(customerId);
// What I can do
customer.Contracts = _customerServices.GetCustomerContracts(customer);
// Now what I'd like to do
customer.GetContracts(); // <-- This would mean I can call when needed from the object
}
The reason is that the Customer object may be passed in other services, let's say SendMessageServices or AddReservation or a myriad of other related but distinct things.
This way, I can pass into ContractServices.VacateContract(customer) the Customer object, and from there call the GetContracts() from the object, without having to DI CustomerServices into ContractServices.
It's in theory at the moment, so nothing set in stone, but would be useful to encapsulate all required functionality into the instance of the Customer class.
Related
Here is the scenario:
I'm writing a test for my controller and need to setup a view model titled CheckoutViewModel. My controller method, Products does not take CheckoutViewModel as a parameter, so I cannot pass it in that way.
Currently, the test fails returning a Null Exception because CheckoutViewModel is not getting set and called.
Question: How can I setup my CheckoutViewModel with data.
Error Details:
System.NullReferenceException
Object reference not set to an instance of an object
Current Test
[TestMethod]
public void Products_ProductControllerIsCalled_ReturnsViewWithProducts()
{
// Arrange
var currentSession = _autoMoqer.GetMock<ICurrentSession>().Object;
ProductController productController = new ProductController(currentSession);
var checkoutViewModel = new CheckoutViewModel
{
CheckoutId = new Guid()
};
// Act
ActionResult result = productController.Products();
// Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
Controller
[AccectReadVerbs]
public ActionResult Products()
{
CheckoutViewModel checkoutViewModel = GetCheckoutViewModel();
var checkoutId = checkoutViewModel.CheckoutId;
var result = _productOrchestrator.Products(checkoutId, currentSession)
return View(result);
}
Failing on this method
private CheckoutViewModel GetCheckoutViewModel()
{
if(Session["CheckoutViewModel"] == null)
{
return new CheckoutViewModel();
}
return (CheckoutViewModel)Session["CheckoutViewModel"];
}
If GetCheckoutViewModel has some dependencies on i.e services, dbConnection or other complex classes, you need to add a class with an interface, move the method for GetCheckOutViewModel to the class and take the new interface as a dependency to the controller. Then you need to mock the new interface.
Or edit your viewmodel to take interface dependencies on the stuff that stands in the way of unit testing, i.e the Session.
I think you could create some interface:
public interface ISessionManager
{
Session session {get; set;}
}
Then your controller constructor:
public ProductsController(ISessionManager sm)
{
_sessionManager = sm;
}
Then you can pass a mocked instance to your controller.
I'm guessing that the exceptions is due to the fact that when you're running the unit test there will not be any (webserver) session available. What you want do is to isolate your tests from any external dependencies - and a session state that is part of the webserver hosting environment would be an external dependency.
To solve this you need to either mock or stub out the Session object from your test. There are many ways to do this, but the easiest way would be to make Session a public property on the Controller. From your test you would then set the Session to an instance you create within your test.
I have a method CreateAccount to test. I am using Moq for the same.
Under CreateAccount method, there are multiple table insertion methods which belongs to two classes AccountRepository and BillingRepository
I have setup the Moq but don't know how to use multiple moq objects.
Below is some code snippet
Mock<AccountRepository> moq = new Mock<AccountRepository>();
Mock<BillingRepository> moqBill = new Mock<BillingRepository>();
moq.Setup(x => x.AddTable_1(new AddTable_1 { }));
moq.Setup(x => x.AddTable_2(new AddTable_2 { }));
moqBill.Setup(x => x.Table_3());
CreateAccount method takes four parameters and its under ApplicationService class
public class ApplicationService
{
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
obj_1.AddTable_1(objdata_1);
obj_1.AddTable_2(objdata_2);
obj_2.AddTable_3(objdata_3);
}
}
Please suggest some solution. How can these three methods will be skipped ?
Thanks in advance.
You have to provide some means to inject obj_1 and obj_2, since they seem to represent your instances of AccountRepository and BillingRepository, resp.
Typically, you might want to do this by using constructor injection. Extending the snippet you provided, this might look like this:
public class ApplicationService
{
private readonly AccountRepository _accountRepository;
private readonly BillingRepository _billingRepository;
public ApplicationService(AccountRepository accountRepository, BillingRepository billingRepository)
{
_accountRepository = accountRepository;
_billingRepository = billingRepository;
}
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
_accountRepository.AddTable_1(objdata_1);
_accountRepository.AddTable_2(objdata_2);
_billingRepository.AddTable_3(objdata_3);
}
}
Now you can inject your mocks into the class under test:
public void CreateAccount_WhenCalledLikeThis_DoesSomeCoolStuff()
{
var accountRepoMock = new Mock<AccountRepository>();
// set it up
var billingRepository = new Mock<BillingRepository>();
// set it up
var appService = new ApplicationService(accountRepoMock.Object, billingRepoMock.Objcet);
// More setup
// Act
var response = appService.CreateAccount(...);
// Assert on response and/or verify mocks
}
I have created a generic repository for my entity types which handles retrieving , adding and deleting data. Each entity type has a corresponding Service class which interacts with the generic repository to handle all the Data access.
However many times i need to retrieve data based on more than one service and i am never sure where to place this code. For example below is some code that returns a list of email addresses ("GetEmailTrackingAddressGroup" function) which is using 3 different service. I have placed this in the "GroupService" but it could also easily go in the "UserService" aswell.
public class GroupService
{
IRepository<Group> groupRepository;
public GroupService(IRepository<Group> groupRepository)
{
this.groupRepository = groupRepository;
}
public Group GetById(int id)
{
return groupRepository.GetSingle(g => g.Id == id);
}
public static List<string> GetEmailTrackingAddressesGroup(int instanceId, int groupId)
{
MyEntities entityContext = new MyEntities();
UserGroupService userGroupService =
new UserGroupService(new BaseRepoistory<UserGroup>(entityContext ));
UserService userService =
new UserService(new BaseRepoistory<User>(entityContext ));
List<string> emails = new List<string>();
Group productGroup = GetById(groupId);
foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
{
if (userGroup.EmailTracking)
emails.Add(userService.GetByUserId(userGroup.UserId).UserName);
}
return emails;
}
}
My Question is, should you just try and pick the most relevant service and place the code in there and call the other relevant service inside it, or should i create a new class which handles Data access when more than 1 service is involved. For example i have placed code for what this class might look like below.
public class DataFunctions
{
public static List<string> GetEmailTrackingAddressesGroup(int instanceId, int groupId)
{
MyEntities entityContext = new MyEntities();
GroupService userGroupService =
new GroupService(new BaseRepoistory<Group>(entityContext ));
UserGroupService userGroupService =
new UserGroupService(new BaseRepoistory<UserGroup>(entityContext ));
UserService userService =
new UserService(new BaseRepoistory<User>(entityContext ));
List<string> emails = new List<string>();
Group productGroup = GetById(groupId);
foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
{
if (userGroup.EmailTracking)
emails.Add(userService.GetByUserId(userGroup.UserId).UserName);
}
return emails;
}
}
The second approach seems to make more sense as this means each service will never rely on other services however im not sure if i am going about this the right way. One concern i have about using this separate class is that it will get very big and hard to manage.
Edit - For now i have come up with a third solution, i think it is better than my previous two however i'm still uncertain if i am managing this correctly. I have created a seperate "EmailService" which will handle all data queries which are needed when handing email functionality in my main ASP.Net web project.
Below is the code for this new class
//Functionality realting to data needed when handling emails
public class EmailService
{
MyEntities entityContext;
AspUserService aspUserService;
GroupService groupService;
UserGroupService userGroupService;
public EmailService()
{
entityContext = new MyEntities ();
aspUserService = new AspUserService(new RepositoryBase<aspnet_Users>(entityContext));
groupService = new GroupService(new RepositoryBase<Group>(entityContext));
userGroupService = new UserGroupService(new RepositoryBase<UserGroup>(entityContext));
}
public List<string> GetEmailsForProductGroup(int groupId)
{
List<string> emails = new List<string>();
Group productGroup = groupService.GetById(groupId);
foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
{
if (userGroup.EmailTracking)
emails.Add(aspUserService.GetByUserId(userGroup.UserId).UserName);
}
return emails;
}
}
If you were to do maintenance on an application you've never seen before, where would you expect it to be? Since it is something that "belongs" to a group, i think that putting it in the group-repo/service would be the right approach. In your example, i would suggest creating a new group-repository, extending the IRepository of group, and create a method for getting the group object, including the connection entities. When scaling your application, this will make a huge difference, since you wont have to query the database for every subobject (n+1 problem).
No offense, but all your approaches suck for the following reasons: tight coupling and messing around responsibilities.
The generic repository is an anti pattern, stay away from it. Your first approach pretty much does the work of a repository and it's a static function (why?!).
The services shouldn't be coupled to concrete repositories. All dependencies should be injected as abstractions via constructor. I get the feeling that aspUsersService,GroupService and UserGroupService (what's the difference????) are actually implemented like a repository and thus, they are useless.
Actually from what I see, all your services are practically repositories. Cut the useless code, have one service/repository that uses directly EF and that's it.
It looks like you're a little confused between the point of repositories and service classes. Your service classes are just repositories. You ended up having to work this way because of the generic repository pattern. This pattern is actually an anti-pattern. It seems nice at first until you get to the point you're at. Instead you should create repositories for each of your entities that handle all CRUD operations for that entity. In these repositories is where you'll place your 'GetEmailTrackingAddressesGroup' type methods. And then your service layer can handle interacting with more that one repository if need be. Your service classes shouldn't have hard coded instances of your repositories. Instead you should be inject repository interfaces into your service's constructor.
Here's an example of how I would set up repositories and a simple service that interacts with 2 repositories.
public interface IUserRepository
{
void Insert(User user);
...
IEnumerable<User> GetByDepartmentId(int deptId);
}
public interface IContactLogRepository
{
void Insert(ContactLog contactLog);
}
public class EmailService
{
private readonly IUserRepository _userRepo;
private readonly IContactLogRepository _contactLogRepo;
public EmailService(IUserRepository userRepo, IContactLogRepository contLogRepo) {
_userRepo = userRepo;
_contactLogRepo = contLogRepo;
}
public void EmailDepartment(int deptId, string message) {
var employees = _userRepo.GetByDepartmentId(deptId);
foreach (var emp in employees) {
Email(emp.Email, message);
_contactLogRepo.Insert(new ContactLog {
EmployeeId = emp.Id,
Message = message
});
}
}
private void Email(string address, string message) {
...
}
}
So our repositories are there to handle CRUD operations for a specific entity - not our service layer. The generic repository pattern forces us to do CRUD (well at least the retrieval) in our services.
I have a class LkCredentials, which is used to store data from SQL table.
[Table(Name = "Credentials")]
public class LkCredentials : LkTable
{
// Database fields
[Column(Name = "id", IsPrimaryKey = true)]
public Binary Uid { get; set; }
...
// Used for dependency injection through Ninject
public ICustomer Customer { get; set; }
public LkCredentials(ICustomer Customer)
{
this.Customer = Customer;
}
// Data loader from database
public void Load(string login)
{
var user = (new SqlTRepository<LkCredentials>()).DBObject.Where(x => x.Login == login).Single();
... // copying data from user to this
}
I'm using Ninject to inject proper ICustomer class this way:
// Create new instance for correct constructor to run and Ninject to resolve
var cred = new LkCredentials((ICustomer)null);
// Load data from database
cred.Load(model.UserName);
But in the process of loading data (void Load), in the variable user new instance of LkCredentials is created, and compiler demands parameterless constructor to be defined. If I create parameterless constructor, then it will be used to create new instance of LkCredentials, but Ninject will not bind correct class - cause constructor incorrect :( And NullReference exception will be raised.
I tried to create constructors chain:
public LkCredentials() : this((ICustomer)null)
{ }
But it didn't work.
What I can do for Ninject to work properly? Any ideas?
P.S.:
Ninject installed as MVC Extension.
Ninject injection in controllers works great, with the same bindings.
Ninject bindings from NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICustomer>().ToProvider<ObjectProvider<ICustomer, Customer, Customer82>>();
kernel.Bind<IAddress>().ToProvider<ObjectProvider<IAddress, Address, ContactInfo>>();
}
public class ObjectProvider<T1,T2,T3> : IProvider
{
public Type Type { get { return typeof(T1); } }
public object Create(IContext context)
{
var securityInfo = context.Kernel.Get<SecurityInformation>();
if (securityInfo.isAuthenticated & securityInfo.DatabaseType == "81")
return context.Kernel.Get<T2>();
else if (securityInfo.isAuthenticated & securityInfo.DatabaseType == "82")
return context.Kernel.Get<T3>();
else
return context.Kernel.Get<T2>();
}
}
I am a student of Ninject and like it a lot. I think the issue is you need to bind LkCredentials to an ILkCredentials and bind it with a parameter. Something like this:
Bind<ILkCredentials>().To<LkCredentials>().WithConstructorArgument("Customer", "Customer");
In the WithConstructorArgument(, ). It's a little confusing because your parameter name is also the name of the object you want to inject.
Here's another example where the parameter name is "name" and the constructor argument is "Fender":
Bind<IGuitar>().To<Guitar>().WithConstructorArgument("name", "Fender");
Hope that helps.
I left parameterless constructor as is, but at the first point, where a need a Customer, I added:
if (this.Customer == null)
this.Customer = (ICustomer)System.Web.Mvc.DependencyResolver.Current.GetService(typeof(ICustomer));
It was enough.
Great thanks to Stephen Byrne, he gives me great advice!
I have a void method, which i need to unit test, can some one please help me how to do it
[TestMethod()]
public void ProcessProductFeedTest()
{
// TODO: Initialize to an appropriate value
ProductDataServiceProvider target = new ProductDataServiceProvider();
target.ProcessProductFeed();
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
in the above code ProcessProductFeed() is a void method which gets some data from SQL server DB and publish to TIBCO, how can i write a unit test case for the same
Well, you should test that the data is published to TIBCO, basically.
For any method, your tests should either test that the value returned is correct if the primary purpose is to compute something, or that the appropriate side-effects have occurred if that's the primary purpose of the method. (You then test the error conditions as well, of course.)
Without knowing anything about either TIBCO or your architecture, I can't really comment on how you go about testing the publishing part. I would personally separate out the three stages of reading, processing and publishing - then each part can be tested in isolation from the others.
Abstract persistence and TIBCO communication from your class. E.g. you can use some repository interface for communication with SQL server :
public interface IProductsRepository
{
IEnumerable<Product> GetSomeProducts();
// other members
}
And some gateway for communcation with TIBCO (I named it Stock, but you should provide business specific names):
public interface IStockGateway
{
void DoSomethingWithProducts(IEnumerable<Product> products);
// other members
}
Then make your class depend on these abstractions. You will be able to mock them and verify class behavior:
public class ProductDataServiceProvider
{
private IProductsRepository _productRepository;
private IStockGateway _stockGateway;
// inject implementations
public ProductDataServiceProvider(
IProductRepository productRepository,
IStockGateway stockGateway)
{
_productRepository = productRepository;
_stockGateway = stockGateway;
}
public void ProcessProductFeed()
{
// use repository and gateway
}
}
Now, back to test. What are responsibilities of your provider - get some products from product repository (implementation of this repository will load products from SQL database) and pass them to gateway (implementation of gateway will publish products to TIBCO). Here is test which uses Moq library:
[TestMethod]
public void ShouldPassSomeProjectToStock()
{
// Arrange
var products = new List<Product>() { }; // create some products
var mockRepository = new Mock<IProductRepository>();
mockRepository.Setup(r => r.GetSomeProducts()).Returns(products);
var mockGateway = new Mock<IStockGateway>();
mockGateway.Setup(g => g.DoSomethingWithProducts(products));
var provider = new ProductDataServiceProvider(mockRepository.Object,
ockGateway.Object);
// Act
provider.ProcessProductFeed();
// Assert
mockRepository.VerifyAll(); // verify products retrieved from repository
mockGateway.VerifyAll(); // verify products passed to gateway
}