I have been reasing about owned instances and I have a need to set one up.
http://docs.autofac.org/en/latest/advanced/owned-instances.html
I have 2 methods in the same class that use this owned instance. I have set it up like this:
private readonly Func<SessionDetails, Owned<ITroposUnitOfWork>> _paramatizedTroposUnitOfWork;
My unit of work class constructor looks like this:
/// <summary>
/// Used for creating manual sessions
/// </summary>
/// <param name="sessionDetails">The details of the session to be created</param>
public TroposUnitOfWork(SessionDetails sessionDetails)
{
// Throw if we don't supply any details
ThrowIf.ArgumentIsNull(() => sessionDetails);
// Start the session
StartSession(sessionDetails);
}
So, my understanding is that if I use a using block, then the unit of work will be disposed of at the end of the call. But it isn't.
Like I mentioned before, I have 2 methods that use this owned instance. They are:
/// <summary>
/// Creates the Tropos user
/// </summary>
/// <param name="model">The user to be created</param>
/// <param name="password">The password to set</param>
private async Task CreateTroposUserAsync(User model, string password)
{
// If there is no password, throw an error
ThrowIf.ArgumentIsNull(() => password);
// Get our master user
var user = await base.FindByNameAsync(model.Master);
// If we have no master user, throw an error
if (user == null) throw new ObjectNotFoundException();
// Create our session details
var sessionDetails = _troposSession.Value.Create(user);
// User our new user
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
try
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Create our user
var transaction = userService.Create(model);
// Save our changes (Don't throw an error if the user already exists)
troposUnitOfWork.Value.RunTransaction(transaction);
} catch (Exception ex)
{
// Display human readable messages
throw new Exception(ex.Message);
}
}
// Sets the new users password
SetTroposPassword(model, password);
// Update the flag
model.HasTroposLogin = true;
}
And the other one is:
/// <summary>
/// Sets the tropos password
/// </summary>
/// <param name="model">The user that needs the password setting</param>
/// <param name="password"></param>
private void SetTroposPassword(User model, string password)
{
// Create our session details
var sessionDetails = _troposSession.Value.Create(model.UserName);
// Create our anonymous session
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Set our password
var transaction = userService.ChangePassword(password);
// Save our changes
troposUnitOfWork.Value.RunTransaction(transaction);
}
}
The first method does invoke the second method, but outside the using block. I put a breakpoint in the TroposUnitOfWork dispose method and it only gets hit once. The constructor is only hit once too.
Does anyone know why?
We need to see the initialization of _paramatizedTroposUnitOfWork.
What class has the CreateTroposUserAsync method? We need to see the constructor of that class. I suppose your overall goal is to get a unit of work implementation.
The reason why you only hit constructor once, might be due to the lifetime you used when registering. If its Owned. Then the two methods probably execute in the same lifetime scope and the dependency is only resolved once. Or put differently the _paramatizedTroposUnitOfWork(sessionDetails) returns the same instance.
I've solved similar problem using decorators and factories
public interface IEventHandlerFactory<in TNotification> where TNotification
: class, IAsyncNotification
{
IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope );
}
public class EventHandlerFactory<TNotification, TEventHandler> :
IEventHandlerFactory<TNotification>
where TNotification : class, IAsyncNotification
where TEventHandler : class, IAsyncNotificationHandler<TNotification>
{
public IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope )
{
return lifetimeScope.ResolveNamed<TEventHandler>( "EventHandler" )
as IAsyncNotificationHandler<TNotification>;
}
}
full .net fiddle is here https://dotnetfiddle.net/fw4IBw
Related
So I have a typical three tiered application layered as below
DAL -> Repository -> Business -> Web.UI/API
I have been reading this article about registering dependencies by centralizing them via modules.
The web layer only has a reference to Business which only has a reference to the Repo which only has a reference to the lowest DAL layer. In this topology since the UI/API layer knows nothing about the Repository and has no reference to it, I can't register the modules in the Repository in the UI/API layer. Similarly I can't register the modules present in the DAL in the Business layer. What I want to do is start the registration process in the top most layer which then sets off a cascading effect of registrations in subsequent layers.
Typically what this would look like is each layer exposing a RegisterAllModules method and somehow trigger the RegisterAllModules method from the layer below it. Has something like this been done? Or is there another way to do this? At this point I don't know if I should roll my own logic out as I mentioned here above, since I don't know if there is a documented way to do something like this or not. Thoughts on how to best go forward here is what I am looking for.
Thanks.
Mmmm... I don't know if what follows is a proper response, but I'm going to try to give you the tools for a solution that suits your exact requirementes.
have you looked into json/xml module configuration? You do not need to know the assemblies through cross reference, you just need to know the name of the assemblies in app.config (or web.config). E.g: you can register one module for Repositories in the Repo assembly and one module for Business services in the Business.dll. This completely removes the need of cross-referencing the various assemblies (for Module scanning, you will still need references for method calls, but that is expected anyway). See here for details: http://docs.autofac.org/en/latest/configuration/xml.html#configuring-with-microsoft-configuration
if you want to enforce no call is done from (say) UI to Repo, you can leverage the "Instance Per Matching Lifetime Scope" function (see http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope). You can use that registration method in order to enforce a Unit-of-work approach. E.g: a Repository can only be resolved in a "repository" LifetimeScope, and only Business components open scopes tagged "repository".
an alternative approach to tagged scopes is in using the "Instance per Owned<>" pattern. In this way, each Business service would require an Owned<Repository>.
Something like:
var builder = new ContainerBuilder();
builder.RegisterType();
builder.RegisterType().InstancePerOwned();
AFAICT, a correct approach would be to register the components through Modules, referenced by the Json/Xml config, and each Module should target specific LifetimeScopes.
When you a class calls the underlying layer, it should open a new LifetimeScope("underlying layer").
I will elaborate further, if you want advice on implementation strategies.
Best,
Alberto Chiesa
Edit:
I didn't knew the "composition root" meaning. Well, thanks for the info!
I favor a SIMPLE configuration file (be it the .config file or a separate .json or .xml file), because I feel that a list of modules to be imported is simpler done through a list than through a class. But this is opinion.
What is not an opinion is that you can import modules from assembly that are not referenced by the "Composition Root" assembly, in a simple and tested way.
So, I would go for Modules for every component registration, but for a textual configuration file for Module registration. YMMV.
Now, let me show you an example of the Unit of Work pattern that I'm using in many live projects.
In our architecture we make heavy use of a Service Layer, which holds responsibility for opening connections to the db and disposing them when finished, etc.
It's a simpler design than what you're after (I prefer shallow other than deep), but the concept is the same.
If you are "out" of the Service Layer (e.g. in an MVC Controller, or in the UI), you need a ServiceHandle in order to access the Service layer. The ServiceHandle is the only class that knows about Autofac and is responsible for service resolution, invocation and disposal.
The access to the Service Layer is done in this way:
non service classes can require only a ServiceHandle
invocation is done through _serviceHandle.Invoke(Func)
Autofac injects the ready to use handles via constructor injection.
This is done through the use of BeginLifetimeScope(tag) method, and registering services (in a module) in this way:
// register every service except for ServiceBase
Builder.RegisterAssemblyTypes(_modelAssemblies)
.Where(t => typeof(IService).IsAssignableFrom(t) && (t != typeof(ServiceBase)))
.InstancePerDependency();
// register generic ServiceHandle
Builder.RegisterGeneric(typeof(ServiceHandle<>))
.AsSelf()
.AsImplementedInterfaces()
.InstancePerDependency();
And registering every shared resource as InstancePerMatchingLifetimeScope("service")
So, an example invocation would be:
... in the constructor:
public YourUiClass(ServiceHandle<MyServiceType> myserviceHandle)
{
this._myserviceHandle = myserviceHandle;
}
... in order to invoke the service:
var result = _myserviceHandle.Invoke(s => s.myServiceMethod(parameter));
This is the ServiceHandle implementation:
/// <summary>
/// Provides a managed interface to access Model Services
/// </summary>
/// <typeparam name="TServiceType">The Type of the parameter to be managed</typeparam>
public class ServiceHandle<TServiceType> : IServiceHandle<TServiceType> where TServiceType : IService
{
static private readonly ILog Log = LogManager.GetLogger(typeof(ServiceHandle<TServiceType>));
private readonly ILifetimeScope _scope;
/// <summary>
/// True if there where Exceptions caught during the last Invoke execution.
/// </summary>
public bool ErrorCaught { get; private set; }
/// <summary>
/// List of the errors caught during execution
/// </summary>
public List<String> ErrorsCaught { get; private set; }
/// <summary>
/// Contains the exception that was thrown during the
/// last Invoke execution.
/// </summary>
public Exception ExceptionCaught { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="scope">The current Autofac scope</param>
public ServiceHandle(ILifetimeScope scope)
{
if (scope == null)
throw new ArgumentNullException("scope");
_scope = scope;
ErrorsCaught = new List<String>();
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <param name="command">
/// Void returning action to be performed
/// </param>
/// <remarks>
/// The implementation simply wraps the Action into
/// a Func returning an Int32; the returned value
/// will be discarded.
/// </remarks>
public void Invoke(Action<TServiceType> command)
{
Invoke(s =>
{
command(s);
return 0;
});
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <typeparam name="T">Type of the data to be returned</typeparam>
/// <param name="command">Action to be performed. Returns T.</param>
/// <returns>A generically typed T, returned by the provided function.</returns>
public T Invoke<T>(Func<TServiceType, T> command)
{
ErrorCaught = false;
ErrorsCaught = new List<string>();
ExceptionCaught = null;
T retVal;
try
{
using (var serviceScope = GetServiceScope())
using (var service = serviceScope.Resolve<TServiceType>())
{
try
{
retVal = command(service);
service.CommitSessionScope();
}
catch (RollbackException rollbackEx)
{
retVal = default(T);
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(rollbackEx);
Log.InfoFormat(rollbackEx.Message);
ErrorCaught = true;
ErrorsCaught.AddRange(rollbackEx.ErrorMessages);
ExceptionCaught = rollbackEx;
DoRollback(service, rollbackEx.ErrorMessages, rollbackEx);
}
catch (Exception genericEx)
{
if (service != null)
{
DoRollback(service, new List<String>() { genericEx.Message }, genericEx);
}
throw;
}
}
}
catch (Exception ex)
{
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(ex);
var msg = (Log.IsDebugEnabled) ?
String.Format("There was an error executing service invocation:\r\n{0}\r\nAt: {1}", ex.Message, ex.StackTrace) :
String.Format("There was an error executing service invocation:\r\n{0}", ex.Message);
ErrorCaught = true;
ErrorsCaught.Add(ex.Message);
ExceptionCaught = ex;
Log.ErrorFormat(msg);
retVal = default(T);
}
return retVal;
}
/// <summary>
/// Performs a rollback on the provided service instance
/// and records exception data for error retrieval.
/// </summary>
/// <param name="service">The Service instance whose session will be rolled back.</param>
/// <param name="errorMessages">A List of error messages.</param>
/// <param name="ex"></param>
private void DoRollback(TServiceType service, List<string> errorMessages, Exception ex)
{
var t = new Task<string>
service.RollbackSessionScope();
}
/// <summary>
/// Creates a Service Scope overriding Session resolution:
/// all the service instances share the same Session object.
/// </summary>
/// <returns></returns>
private ILifetimeScope GetServiceScope()
{
return _scope.BeginLifetimeScope("service");
}
}
Hope it helps!
I wonder if any one can point me in the right direction?
I am using the UOW repository pattern and have my dependencies injected via Ninject. I have a UnitOfWorkMapping class which inherits from NinjectModule, which I use to bind my IUnitOfWork to a concrete implementation of Dbcontext, see below
public class UnitOfWorkMapping : NinjectModule
{
public override void Load()
{
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
// Objects that explicitly need a DB context for the life of the request
Bind<IUnitOfWorkInRequestScope>()
.To<WebsiteDbContext>()
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
// Objects that specificall need a DB context for the life of the application
Bind<IUnitOfWorkInApplicationScope>()
.To<WebsiteDbContext>()
.InSingletonScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
}
}
So this gets called when the application starts and provides my site user with a context for an unauthorised user. This context has a database connection which connects to a database User which has limited access to the database objects.
Once the user has logged in to site I would like to switch to another connection which will give the context access to a Database user with wider access to the database objects.
So by the time the code execution reaches the true condition block for "if (Request.IsAuthenticated)" it is already using the "authorised" database connection for the context.
/// <summary>
/// Handles the PostAuthenticateRequest event of the Application control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
String[] roles;
var applicationConfiguration =
(IApplicationConfiguration)
DependencyResolver.Current.GetService(typeof(IApplicationConfiguration));
var identity = HttpContext.Current.User.Identity;
if (Request.IsAuthenticated)
{
var roleRepository =
(IRoleRepository)DependencyResolver.Current.GetService(typeof(IRoleRepository));
roles = roleRepository.GetRolesForUser(identity.Name);
}
else
{
roles = new[] { applicationConfiguration.UnknownUserRoleName };
}
var webIdentity = new WebIdentity(identity, roles);
var principal = new WebsitePrincipal(webIdentity)
{
ApplicationConfiguration = applicationConfiguration
};
HttpContext.Current.User = principal;
}
I haven't been able to find a code example on the net that is close enough to my code to adapt and implement. Please can any one advise? Thank you in advance.
A link to the complete code can be provided if required.
Solution:
Ok, so with the tireless help of Erik we have got there. The solution I am using is as so..
public class UnitOfWorkMapping : NinjectModule
{
public override void Load()
{
// Bind the IUnitOfWork for a user that is not logged in.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(request => IsUserAuthenticated(request))
.WithConstructorArgument(
"connectionString", ConfigurationManager.ConnectionStrings[ConnectionStringKeys.MainUserConnectionString]
.ConnectionString);
// Bind the IUnitOfWork for a user that is not logged in.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(request => !IsUserAuthenticated(request))
.WithConstructorArgument(
"connectionString", ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
}
/// <summary>
/// Determines if the user authenticated.
/// </summary>
/// <param name="request">The Ninject Activation request.</param>
/// <returns>
/// returns <c>true</c> if the user exists and is authenticated
/// </returns>
public Boolean IsUserAuthenticated(IRequest request)
{
return (
(HttpContext.Current.User != null) &&
HttpContext.Current.User.Identity.IsAuthenticated);
}
}
I hope that helps someone not spend days trying to get to the bottom of this issue.
If you want to use different bindings for whether a user is logged in or out, it's very simple.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(x => !HttpContext.Current.Request.IsAuthenticated)
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(x => HttpContext.Current.Request.IsAuthenticated)
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.AuthorisedUser]
.ConnectionString);
Note that these both have the same IUnitOfWork binding, it will simply return the correct one based on whether the user is logged in or not.
I would also do this:
Bind<IIdentity>()
.To<WebIdentity>()
...
Bind<IIdentity>()
.ToMethod(x => HttpContext.Current.User.Identity)
.WhenInjectedInto(typeof(WebIdentity))
...
Bind<IPrincipal>()
.To<WebsitePrincipal>()
...
Then, configure your WebsitePrincipal's constructor to take IIdentity and IApplicationConfiguration parameters, make WebIdentity's constructor take IIdentity and IRoleRepository as parameters (note the .WhenInjectedInto uses the existing Identity). Have your constructors do the work.
Then you need only write the following (DI takes care of the rest):
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpContext.Current.User = DependencyResolver.Current.GetService(typeof(IPrincipal));
}
I have a somewhat simple web app, that uses an ASMX web service as its sole data access. All the information is gotten from it, and saved to it. It works fine so thats out of the way.
I just updated to VS2012, and it complained about the class implementing the service reference, does not inherit from IDisposeable.
After some reading, i am more confused as some solutions are really elaborate, some are simple. Short version is, after understanding so little, it seems like i cant adapt it to how my app is made.
I have several data access classes, all focusing on methods for an area. For example, one dataaccess for customer related calls, one for product related calls etc.
But since they are all using the same service, they all derive from a base data access class that holds the reference.
This is the base data access class:
public class BaseDataAccess
{
private dk.odknet.webudv.WebService1 _service;
private string _systemBrugerID, _systemPassword;
public BaseDataAccess()
{
//Gets the system user and password that is stored in the webconfig file. This means you only have to change
//the username and password in one place without having to change the code = its not hardcoded.
_systemBrugerID = System.Configuration.ConfigurationManager.AppSettings["SystemBrugerID"].ToString();
_systemPassword = System.Configuration.ConfigurationManager.AppSettings["SystemPassword"].ToString();
_service = new dk.odknet.webudv.WebService1();
}
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get { return _service; }
}
/// <summary>
/// Gets the system user id, used for certain methods in the webservice.
/// </summary>
protected string SystemBrugerID
{
get { return _systemBrugerID; }
}
/// <summary>
/// Gets the system user password, used for certain methods in the webservice.
/// </summary>
protected string SystemPassword
{
get { return _systemPassword; }
}
}
And here is how a derived class utilizes the service reference from the base class:
public class CustomerDataAccess : BaseDataAccess
{
public CustomerDataAccess() {}
/// <summary>
/// Get's a single customer by their ID, as the type "Kunde".
/// </summary>
/// <param name="userId">The user's username.</param>
/// <param name="customerId">Customer's "fkKundeNr".</param>
/// <returns>Returns a single customer based on their ID, as the type "Kunde".</returns>
public dk.odknet.webudv.Kunde GetCustomerById(string userId, string customerId)
{
try
{
return Service.GetKunde(SystemBrugerID, SystemPassword, userId, customerId);
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}}
So how on earth do i implement IDisposable in this situation? I just cant wrap my head around it.
EDIT
I have fiddled with the service reference, and come up with this:
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get
{
try
{
using (_service = new dk.odknet.webudv.WebService1())
{
return _service;
}
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
}
Yes the exception handling isnt great, i will get to that (advice is appreciated), but VS2012 does not complain about the lack of IDisposable anymore.
Instantiation of the service has been removed from the constructor. The app works fine without any further modifications.
Will this suffice?
I have a url: like this one: http://www.example/about/49.
I want it to be seen as http://www.example/about/, but I must have this parameters passed as QueryString parameters.
Is it possible ?
Be careful with session variables; it's easy to have multiple pages opened which are all using the same session and end up mixing the values.
It would be better to use TempData, which only allows the value to be used once (removed on first access). However, this implies the value will be used almost immediately.
You can also write a cookie with the desired value, intercept the request (ASP.Net provides a variety of ways of doing this, such as the BeginRequest event), and internally process the URL as though it contained the value.
Of course, you then must cleanup the cookie (which will have the same problem as a Session-based solution). Remember that a cookie is more vulnerable to tampering on the client.
Personally, I think any of these approaches are far more trouble than they are worth. "Hackable URLs" (such as those which contain a potentially meaningful ID) are usually a good thing.
My workaround for this (Which works REALLY well, thanks to the help of the SO Community)
Create a class called SiteSession.cs
Input the following code:
using System;
using System.Collections.Generic;
using System.Web;
/// <summary>
/// Summary description for SiteSession
/// </summary>
public class SiteSession
{
/// <summary>
/// The _site session
/// </summary>
private const string _siteSession = "__SiteSession__";
/// <summary>
/// Prevents a default instance of the <see cref="SiteSession" /> class from being created.
/// </summary>
private SiteSession()
{
}
/// <summary>
/// Gets the current Session
/// </summary>
/// <value>The current.</value>
public static SiteSession Current
{
get
{
SiteSession session = new SiteSession();
try
{
session = HttpContext.Current.Session[_siteSession] as SiteSession;
}
catch(NullReferenceException asp)
{
}
if (session == null)
{
session = new SiteSession();
HttpContext.Current.Session[_siteSession] = session;
}
return session;
}
}
//Session properties
public int PageNumber {get;set;}
}
You can put anything in the Session Properties, just make sure its public.
Then, set it by:
SiteSession.Current.PageNumber = 42
And call it with
int whatever = SiteSession.Current.PageNumber
First of all I wanted to thank all of you for your continuous contributions to the Stack Overflow community! I've been a member of Stack Overflow for years and have come to rely on your input more so than any other source online. Though I try to participate and answer members' questions whenever I can, every once in a while I find myself stuck and in need of help.
Speaking of which I have an unusual code problem. I am writing an API library in C# that needs to be able to be called from WPF/Windows Forms application, but also from within Unit Test code.
The issue is that I need to be able to report (in Excel) on whether each method of the library executed properly when the API is called from within a WPF/windows forms application, along some other metadata and optionally a return type.
When the code is consumed within Unit Tests I don't really care about the reporting, but I do need to be able to produce an Assert on whether the API call executed properly or not.
For instance, if in a Unit Test we have an Test Initialize portion, one of the API calls may be to create a Domain User for the test method to use. Another one may also create a Domain Group, so that the user has proper group membership.
To accomodate the consumption of the API from WPF/WinForms, I've been rewriting every function in the API to return a OperationStep type, with the hopes that when all API calls have executed I would have an IEnumerable<OperationStep> which I can write to a CSV file.
So the question is is there an easier way of achieving what I have done so far? The reporting is extremely tedious and time consuming to code, considering that the API library consists of hundreds of similar methods. Samples are described bellow:
OperationStep<PrincipalContext> createDomainConnectionStep = DomainContext.Current.GetPrincipalContext(settings.DomainInfo);
OperationStep<UserPrincipal> createDomainUserStep = DomainContext.Current.CreateUser(createDomainConnectionStep.Context, settings.TestAccountInfo.Username, settings.TestAccountInfo.Password);
OperationStep<GroupPrincipal> createDomainGroupStep = DomainContext.Current.CreateGroup(createDomainConnectionStep.Context, settings.TestAccountInfo.UserGrupName);
Where the DomainContext is a singleton object whose functionality is to connect to the domain controller and create a user, group, and associate the user to a group.
Note that both the second and the third method call require the output of the first, and therefore warranting the need for having the public T Context within the OperationResult object as described bellow.
The OperationStep object consists of the following properties which are inherited by the IOperation interface with the exception of the public T Context.
public class OperationStep<T> : IOperation
{
/// <summary>
/// Denotes the Logical Name of the current operation
/// </summary>
public string Name { get; set; }
/// <summary>
/// Denotes the stage of execution of the current operation: Setup, Execution, Validation, Cleanup
/// </summary>
public OperationStage Stage { get; set; }
/// <summary>
/// Denotes whether the test step completed properly or failed.
/// </summary>
public OperationResult Result { get; set; }
/// <summary>
/// Denotes the return type of the test method.
/// </summary>
public T Context { get; set; }
/// <summary>
/// Denotes any other relevant information about the test step
/// </summary>
public string Description { get; set; }
/// <summary>
/// If the test step result is failed, this should have the stack trace and the error message.
/// </summary>
public string Error { get; set; }
}
The method calls themselves are a bit bloated and tedious but here is a sample.
public class DomainContext
{
private static volatile DomainContext currentContext;
private static object synchronizationToken = new object();
/// <summary>
/// default ctor.
/// </summary>
private DomainContext() { }
/// <summary>
/// Retrieves the Current DomainContext instance.
/// </summary>
public static DomainContext Current
{
get
{
if (currentContext == null)
{
lock (synchronizationToken)
{
if (currentContext == null)
{
currentContext = new DomainContext();
}
}
}
return currentContext;
}
}
/// <summary>
/// Establishes a connection to the domain.
/// </summary>
/// <param name="domainInfo"></param>
/// <returns></returns>
public OperationStep<PrincipalContext> GetPrincipalContext(DomainInfo domainInfo)
{
OperationStep<PrincipalContext> result = new OperationStep<PrincipalContext>();
result.Name = "Establish Connection to Active Directory";
result.Result = OperationResult.Success;
result.Stage = OperationStage.Setup;
result.Description = string.Format("Domain Name: {0}, Default Containter: {1}", domainInfo.FQDN, domainInfo.Container);
try
{
ContextType contextType = this.GetContextType(domainInfo.DomainType);
PrincipalContext principalContext;
try
{
principalContext = new PrincipalContext(contextType, domainInfo.FQDN, domainInfo.Container);
}
catch
{
throw new Exception("Unable to establish connection to Active Directory with the specified connection options.");
}
if (principalContext != null)
{
bool authenticationResult = principalContext.ValidateCredentials(domainInfo.Username, domainInfo.Password);
if (!authenticationResult)
{
throw new Exception("Unable to authenticate domain admin user to Active Directory.");
}
result.Context = principalContext;
result.Result = OperationResult.Success;
}
}
catch(Exception ex)
{
result.Error = ex.Message;
result.Result = OperationResult.Failure;
}
return result;
}
}
When all method calls have executed theoreticaly I should have an IEnumerable<IOperation> which in the case of a win form I can write in a csv file (to be viewed in MS Excel) or in the case of a unit test I can simply omit the extra info and ignore (other than the method executed successively and the T Context property).
If I understood you correctly - all that OperationSteps are here only for logging. Then why not enable simple .NET logging? Log needed info where it is convenient for you. You can use TraceSource with DelimetedTraceListener to write to .csv file. More than that. You can move logging logic to Strategy class and override its logging methods in your unit test so that instead of logging you call Assert methods.