how is discriminated unions (pure) implemented in f# - f#

I wonder if they are simply using a number to represent each cases, or they will be compiled to control flows (if statements) that helps decide the matter
pure means the cases aren't of any data types
I tried looking for answers on stack overflow but I didn't find any

In general yes, each case is a number (called Tag in the implementation).
It's easy to figure out how some things are realized in F# by decompiling F# assembly to C#.
E.g.
Union type defined like this:
type UnionType = This | That of int
Decompiles to C# like this (I cut out interface implementations at the bottom as it is pretty lengthy, you can easily repeat this process yourself):
[Serializable]
[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
[DebuggerDisplay("{__DebugDisplay(),nq}")]
[CompilationMapping(SourceConstructFlags.SumType)]
public abstract class UnionType : IEquatable<UnionType>, IStructuralEquatable, IComparable<UnionType>, IComparable, IStructuralComparable
{
public static class Tags
{
public const int This = 0;
public const int That = 1;
}
[Serializable]
[SpecialName]
[DebuggerTypeProxy(typeof(_This#DebugTypeProxy))]
[DebuggerDisplay("{__DebugDisplay(),nq}")]
internal class _This : UnionType
{
[CompilerGenerated]
[DebuggerNonUserCode]
internal _This()
{
}
}
[Serializable]
[SpecialName]
[DebuggerTypeProxy(typeof(That#DebugTypeProxy))]
[DebuggerDisplay("{__DebugDisplay(),nq}")]
public class That : UnionType
{
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal readonly int item;
[CompilationMapping(SourceConstructFlags.Field, 1, 0)]
[CompilerGenerated]
[DebuggerNonUserCode]
public int Item
{
[CompilerGenerated]
[DebuggerNonUserCode]
get
{
return item;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
internal That(int item)
{
this.item = item;
}
}
[SpecialName]
internal class _This#DebugTypeProxy
{
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal _This _obj;
[CompilerGenerated]
[DebuggerNonUserCode]
public _This#DebugTypeProxy(_This obj)
{
_obj = obj;
}
}
[SpecialName]
internal class That#DebugTypeProxy
{
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal That _obj;
[CompilationMapping(SourceConstructFlags.Field, 1, 0)]
[CompilerGenerated]
[DebuggerNonUserCode]
public int Item
{
[CompilerGenerated]
[DebuggerNonUserCode]
get
{
return _obj.item;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
public That#DebugTypeProxy(That obj)
{
_obj = obj;
}
}
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static readonly UnionType _unique_This = new _This();
[CompilerGenerated]
[DebuggerNonUserCode]
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
public int Tag
{
[CompilerGenerated]
[DebuggerNonUserCode]
get
{
return (this is That) ? 1 : 0;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
public static UnionType This
{
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
get
{
return _unique_This;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
public bool IsThis
{
[CompilerGenerated]
[DebuggerNonUserCode]
get
{
return this is _This;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
[DebuggerBrowsable(/*Could not decode attribute arguments.*/)]
public bool IsThat
{
[CompilerGenerated]
[DebuggerNonUserCode]
get
{
return this is That;
}
}
[CompilerGenerated]
[DebuggerNonUserCode]
internal UnionType()
{
}
[CompilationMapping(SourceConstructFlags.UnionCase, 1)]
public static UnionType NewThat(int item)
{
return new That(item);
}
/* cut out members including interface implementations */
}

Related

Ninject Property Injection not working on ActionFilters

I have an interface and a class that implements that interface
public interface ISessionVariables
{
bool IsSessionTokenValidated { get; set; }
}
public class HttpContextSessionVariablesAdapter : ISessionVariables
{
private T Get<T>(string key)
{
return (T)HttpContext.Current.Session[key];
}
private void Set(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
public bool IsSessionTokenValidated
{
get { return Get<bool>("IsSessionTokenValidated"); }
set { Set("IsSessionTokenValidated", value); }
}
}
I bind the interface to implementation using Ninject:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISessionVariables>().To<HttpContextSessionVariablesAdapter>();
}
I have an ActionFilter
public class ValidateSessionTokenAttribute : ActionFilterAttribute
{
[Inject]
public ISessionVariables SessionVariables { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (SessionVariables.IsSessionTokenValidated == false)
{
throw new HttpException((int)HttpStatusCode.Unauthorized, "User session has expired");
}
base.OnActionExecuting(filterContext);
}
}
Strange bug:
If i add the ValidateSessionTokenAttribute to an ActionResult, SessionVariables property gets populated.
[ValidateSessionToken]
public ActionResult Index()
{
}
If i bind the same filter to all ActionResults, (in FilterConfig), the SessionVariables property is always null.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ValidateSessionTokenAttribute());
}
}
What i am doing wrong?

MVC3 EF Unit of Work + Generic Repository + Ninject

I'm new to MVC3 and have been following the awesome tutorials on the asp.net website. However, I can't quite wrap my head around how to use Unit of Work and Generic Repository patterns with Ninject. I used this tutorial as a starting point: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Without using interfaces, I know I can implement it like so:
Generic Repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class
{
internal MyContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(MyContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
}
Unit of Work:
private MyContext context = new MyContext();
private GenericRepository<Student> studentRepository;
private GenericRepository<Course> courseRepository;
public GenericRepository<Student> StudentRepository
{
if (this.studentRepository == null)
{
this.studentRepository = new GenericRepository<Student>(context);
}
return studentRepository;
}
public GenericRepository<Course> CourseRepository
{
if (this.courseRepository == null)
{
this.courseRepository = new GenericRepository<Course>(context);
}
return courseRepository;
}
This setup allows me to pass the same context to all repositories, and then call a single Save() function to commit the changes.
I know I can use an interface IGenericRepository<TEntity> and the concrete implementation GenericRepository<TEntity> and then bind them using Ninject:
kernel.Bind(typeof(IGenericRepository<>)).To(typeof(GenericRepository<>));
But how would I go about setting up my IUnitOfWork and UnitOfWork to ensure that all my repositories share a single database context? Am I even doing it right in the first place? I've searched around but all I seem to find are tutorials that only use generic repositories without a unit of work.
Your base Repo:
public class BaseRepository<TObject> : IRepository<TObject>
where TObject : class
{
public BaseRepository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null) throw new ArgumentException("unitOfWork");
UnitOfWork = unitOfWork;
}
protected DbSet<TObject> DbSet
{
get
{
return Context.Set<TObject>();
}
}
public void Dispose()
{
if (sharedContext && (Context != null))
Context.Dispose();
}
public virtual IQueryable<TObject> All()
{
return DbSet.AsQueryable();
}
public virtual IQueryable<TObject>
Filter(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Where(predicate).AsQueryable<TObject>();
}
public virtual IQueryable<TObject> Filter<Key>(Expression<Func<TObject, Key>> sortingSelector, Expression<Func<TObject, bool>> filter, out int total,
SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50)
{
int skipCount = index * size;
var _resultSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable();
total = _resultSet.Count();
_resultSet = sortby == SortingOrders.Asc ? _resultSet.OrderBy(sortingSelector).AsQueryable() : _resultSet.OrderByDescending(sortingSelector).AsQueryable();
_resultSet = skipCount == 0 ? _resultSet.Take(size) : _resultSet.Skip(skipCount).Take(size);
return _resultSet;
}
public bool Contains(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Count(predicate) > 0;
}
public virtual TObject Find(params object[] keys)
{
return DbSet.Find(keys);
}
public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
{
return DbSet.FirstOrDefault(predicate);
}
public virtual TObject Create(TObject TObject, bool SaveChanges = true)
{
var newEntry = DbSet.Add(TObject);
if (!sharedContext && SaveChanges)
Context.SaveChanges();
return newEntry;
}
public virtual int Count
{
get
{
return DbSet.Count();
}
}
public virtual int Delete(TObject TObject)
{
DbSet.Remove(TObject);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
public virtual int Update(TObject TObject, bool SaveChanges = true)
{
var entry = Context.Entry(TObject);
DbSet.Attach(TObject);
entry.State = EntityState.Modified;
if (!sharedContext && SaveChanges)
return Context.SaveChanges();
return 0;
}
public virtual int Delete(Expression<Func<TObject, bool>> predicate)
{
var objects = Filter(predicate);
foreach (var obj in objects)
DbSet.Remove(obj);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
/// <summary>
/// Sets the state of an entity.
/// </summary>
/// <param name="entity">object to set state.</param>
/// <param name="entityState"><see cref="EntityState"/></param>
protected virtual void SetEntityState(object entity, EntityState entityState)
{
Context.Entry(entity).State = entityState;
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
protected virtual void Attach(object entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
Context.Entry(entity).State = EntityState.Modified;
}
protected virtual void Detach(object entity)
{
Context.Entry(entity).State = EntityState.Detached;
}
public void SubmitChanges()
{
UnitOfWork.SaveChanges();
}
#region Properties
private bool sharedContext { get; set; }
/// <summary>
/// Unit of work controlling this repository.
/// </summary>
protected IUnitOfWork UnitOfWork { get; set; }
/// <summary>
/// Provides access to the ef context we are working with
/// </summary>
internal IMyContext Context
{
get
{
return (IMyContext)UnitOfWork;
}
}
#endregion
}
Notice Context is an interface implementing UnitOfWork.
Your context interface:
public interface IMyContext : IDbContext
{
DbSet<Sometype> SomeProperty { get; set; }
...
}
Your IDbContext interface:
public interface IDbContext
{
DbChangeTracker ChangeTracker { get; }
DbContextConfiguration Configuration { get; }
Database Database { get; }
void Dispose();
void Dispose(bool disposing);
DbEntityEntry Entry(object entity);
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
bool Equals(object obj);
int GetHashCode();
Type GetType();
IEnumerable<DbEntityValidationResult> GetValidationErrors();
void OnModelCreating(DbModelBuilder modelBuilder);
int SaveChanges();
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet Set(Type entityType);
bool ShouldValidateEntity(DbEntityEntry entityEntry);
string ToString();
DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items);
}
Then the actual context implementation:
public class MyContext : DbContext, IUnitOfWork, IMyContext
{
//public MyContext()
//{
// Database.SetInitializer<ReconContext>(null);
//}
public ReconContext()
: base("Name=ReconContext")
{
((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = false;
}
public DbSet<SomeType> SomeProperty { get; set; }
....
public new void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SomePropertyMap());
.....
base.OnModelCreating(modelBuilder);
}
int IUnitOfWork.SaveChanges()
{
return base.SaveChanges();
}
void IDisposable.Dispose()
{
base.Dispose();
}
public new void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public new bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return base.ShouldValidateEntity(entityEntry);
}
public new DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
return base.ValidateEntity(entityEntry, items);
}
}
Then in your ninject config you simply do:
kernel.Bind<IUnitOfWork<MyContext>>().To<MyContext>().InRequestScope();

Grails: Integrating the Underlying Java(Spring+Hibernate) web app : sub-object not showing it s fields in form

The problem is since my POJO/Domain classes are in java (obvious actually but just to make it clear) the sub-object that i use does'nt show up properly in the form when I click on available controllers: Eg: Patient has address -->> address is a sub-object in patient:
The address field shows a blank dropdown. How do i resolve this?
I am using def scaffold= in the controller.
package pojo;
public class Address {
private int id;
private String street;
private String city;
private String state;
private String country;
private Integer clinicid;
private Integer patientid;
public Address(){
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Integer getClinicid() {
return clinicid;
}
public void setClinicid(Integer clinicid) {
this.clinicid = clinicid;
}
public Integer getPatientid() {
return patientid;
}
public void setPatientid(Integer patientid) {
this.patientid = patientid;
}
}
package pojo;
public class Clinic {
private int id ;
private String clinicname ;
private Address address;
public int contactNumber;
public Clinic() {
address=new Address();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getClinicname() {
return clinicname;
}
public void setClinicname(String clinicname) {
this.clinicname = clinicname;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
address.setClinicid(this.id);
this.address = address;
}
public int getContactNumber() {
return contactNumber;
}
public void setContactNumber(int contactNumber) {
this.contactNumber = contactNumber;
} }
package pojo;
import java.util.*;
public class Doctor {
private int id ;
private Date dateOfBirth ;
private String username;
private String password ;
//private String name ;
private int contactNumber ;
private String specialization ;
public Clinic clinic;
public Doctor(){
clinic=new Clinic();
}
public Clinic getClinic() {
return clinic;
}
public void setClinic(Clinic clinic) {
this.clinic = clinic;
}
/*public Doctor() {
}*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getContactNumber() {
return contactNumber;
}
public void setContactNumber(int contactNumber) {
this.contactNumber = contactNumber;
}
public String getSpecialization() {
return specialization;
}
public void setSpecialization(String specialization) {
this.specialization = specialization;
}
}
package pojo;
import java.util.*;
public class Patient {
private int id;
private Date dateOfBirth;
private Date registrationDate;
private String name ;
public int contactNumber;
public enum sexenum {MALE, FEMALE}
public sexenum sex;
private Address address;
private Clinic clinic;
private Doctor doctor;
public Patient(){
clinic=new Clinic();
doctor=new Doctor();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public Date getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Date registrationDate) {
this.registrationDate = registrationDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getContactNumber() {
return contactNumber;
}
public void setContactNumber(int contactNumber) {
this.contactNumber = contactNumber;
}
public sexenum getSex() {
return sex;
}
public void setSex(sexenum sex) {
this.sex = sex;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
address.setPatientid(this.id);
this.address = address;
}
public Clinic getClinic() {
return clinic;
}
public void setClinic(Clinic clinic) {
this.clinic = clinic;
}
public Doctor getDoctor() {
return doctor;
}
public void setDoctor(Doctor doctor) {
this.doctor = doctor;
}
}
patient [ one to one with ] address;.
clinic [ one to one with ] address;.
doctorid [ foreign key many to one]where ever referenced.
clinic id [foreign key many to one ] where ever referenced.
As far as I know, Grails only supports GORM-mappings (http://grails.org/doc/latest/guide/single.html#ormdsl) and hibernate mappings (http://grails.org/doc/latest/guide/single.html#hibernate)
I don't think the mappings you provide are valid for scaffolding.

How to reuse a transient dependency in same context with Castle Windsor DI container

If I have the following setup, how can I configure my container to use the same database, when objects are created in the same context
public class Database { }
public interface IRepository { Database Database { get; } }
public interface IFooRepository : IRepository { }
public interface IBarRepository : IRepository { }
public class FooRepository : IFooRepository
{
public Database Database { get; private set; }
public FooRepository(Database database) { this.Database = database; }
}
public class BarRepository : IBarRepository
{
public Database Database { get; private set; }
public BarRepository(Database database) { this.Database = database; }
}
public class Consumer
{
public IFooRepository fooRepository { get; private set; }
public IBarRepository barRepository { get; private set; }
public Consumer(IFooRepository fooRepository, IBarRepository barRepository)
{
this.fooRepository = fooRepository;
this.barRepository = barRepository;
}
}
[TestClass]
public class ConfigurationTest
{
private IWindsorContainer container;
[TestMethod]
public void SameDatabaseIsUsed()
{
Consumer consumer = container.Resolve<Consumer>();
IFooRepository fooRepository = consumer.fooRepository;
IBarRepository barRepository = consumer.barRepository;
Assert.AreEqual(fooRepository.Database, barRepository.Database); //FAILS
}
[TestMethod]
public void DifferentDatabaseIsUsed()
{
Consumer consumer = container.Resolve<Consumer>();
IFooRepository fooRepository = consumer.fooRepository;
Consumer consumer2 = container.Resolve<Consumer>();
IBarRepository barRepository = consumer2.barRepository;
Assert.AreNotEqual(fooRepository.Database, barRepository.Database); //PASSES
}
[TestInitialize]
public void SetUp()
{
container = new WindsorContainer();
container.Register(
Component.For<Database>().ImplementedBy<Database>().LifeStyle.Transient,
AllTypes.FromThisAssembly()
.BasedOn<IRepository>().WithService.FromInterface()
.Configure(c => c.LifeStyle.Transient),
Component.For<Consumer>().ImplementedBy<Consumer>().LifeStyle.Transient
);
}
}
EDIT:
I have tried to use a custom lifestyle, but i cannot figure out what I can use to detect that i have switched context
public class DatabaseLifestyleManager : AbstractLifestyleManager
{
private CreationContext context;
private Database database;
private Database Database
{
get
{
if (database == null) database = new Database();
return database;
}
set
{
database = null;
}
}
public override object Resolve(CreationContext context)
{
if (this.context!=null && this.context.??? == context.???)
return Database;
else
{
this.context = context;
Database = null;
return Database;
}
}
public override void Dispose()
{
database = null;
context = null;
}
}
......
Component.For<Database>().ImplementedBy<Database>().LifeStyle.Custom(typeof(DatabaseLifestyleManager)
You always get a new instance when requesting a transient component, if it's not what you want don't use the transient lifestyle :-)
Why would you register a transient component, but attempt to resolve the same object depending on some kind of "context"? Most likely the lifestyle is wrong for the situation, and you will be in trouble trying to coerce it into something it's not.
What you want is something like a contextual lifestyle, mentioned in this article.
The below two gists have an implementation for this:
http://gist.github.com/400979
http://gist.github.com/400980
This will allow you do this:
Register(Component.For<Database>().LifeStyle.Scoped())
[TestMethod]
public void SameDatabaseIsUsed()
{
using (container.BeginScope())
{
Consumer consumer = container.Resolve<Consumer>();
IFooRepository fooRepository = consumer.fooRepository;
IBarRepository barRepository = consumer.barRepository;
Assert.AreEqual(fooRepository.Database, barRepository.Database); // YAY!
}
}
Hope this helps!
A contextual lifestyle is included in the Castle.Windsor.Lifestyles contrib project.
I came up with this solution myself by implementing IDisposable, so that I can use a kind of sessionscope for the Database
Would this be a valid way to handle this situation?
All test passes, but there is some added functionality, that must be implemented in all my future consumers of the repositories:
public class Database { }
public interface IRepository : IDisposable { Database Database { get; } }
public interface IFooRepository : IRepository { }
public interface IBarRepository : IRepository { }
public abstract class BaseRepository : IDisposable
{
public BaseRepository(Database database) { this.Database = database; }
public Database Database { get; private set; }
public void Dispose() { Database = null; }
}
public class FooRepository : BaseRepository, IFooRepository
{
public FooRepository(Database database) : base(database) { }
}
public class BarRepository : BaseRepository, IBarRepository
{
public BarRepository(Database database) : base(database) { }
}
public abstract class BaseConsumer : IDisposable
{
public abstract void Dispose();
}
public class Consumer : BaseConsumer
{
public IFooRepository fooRepository { get; private set; }
public IBarRepository barRepository { get; private set; }
public Consumer(IFooRepository fooRepository, IBarRepository barRepository)
{
this.fooRepository = fooRepository;
this.barRepository = barRepository;
}
public override void Dispose()
{
this.fooRepository.Dispose();
this.barRepository.Dispose();
}
}
[TestClass]
public class ConfigurationTest
{
private IWindsorContainer container;
[TestMethod]
public void SameDatabaseIsUsed()
{
IFooRepository fooRepository;
IBarRepository barRepository;
using (Consumer consumer = container.Resolve<Consumer>())
{
fooRepository = consumer.fooRepository;
barRepository = consumer.barRepository;
Assert.AreEqual(fooRepository.Database, barRepository.Database); //FAILS
}
Assert.IsNull(fooRepository.Database);
Assert.IsNull(barRepository.Database);
}
[TestMethod]
public void DifferentDatabaseIsUsed()
{
IFooRepository fooRepository;
IBarRepository barRepository;
using (Consumer consumer = container.Resolve<Consumer>())
fooRepository = consumer.fooRepository;
Assert.IsNull(fooRepository.Database);
using (Consumer consumer2 = container.Resolve<Consumer>())
barRepository = consumer2.barRepository;
Assert.IsNull(barRepository.Database);
}
[TestInitialize]
public void SetUp()
{
container = new WindsorContainer().Register(
Component.For<Database>().ImplementedBy<Database>().LifeStyle.Singleton,
AllTypes.FromThisAssembly()
.BasedOn<IRepository>().WithService.FromInterface()
.Configure(c => c.LifeStyle.Transient),
Component.For<Consumer>().ImplementedBy<Consumer>().LifeStyle.Transient
);
}
}

Container Resolve based on criteria

I'm trying to implement the Strategy pattern while using Windsor container. Here is what I have:
public class OrderProcessor {
...
public OrderProcessor(ITaxStrategy strategy) {}
public void Process(Order order)
{
order.Tax = strategy.CalcTax(order);
}
}
The problem is, how do I configure my container (other container examples welcome) to have, essentially, criteria for choosing the appropriate dependency. So if I register the following
public class USTaxStrategy : ITaxStrategy { ... }
public class CanadaTaxStrateg : ITaxStrategy { ... }
how do I use the Order.Destination (address) as the criteria for the injected dependency?
Here are a few options, pick the one you like best. I usually use the first one, it's the simplest.
[TestFixture]
public class TaxStrategyTests {
[Test]
public void InjectWithFactory() {
var container = new WindsorContainer();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessor>();
container.AddComponent<ITaxStrategyFactory, TaxStrategyFactory>();
var order = new Order {Country = "US"};
container.Resolve<OrderProcessor>().Process(order);
Assert.AreEqual(10, order.Tax);
}
[Test]
public void InjectWithFactoryFromDictionary() {
var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessor>();
container.Register(Component.For<ITaxStrategyFactory>()
.UsingFactoryMethod(kernel => new TaxStrategyFactory2(new Dictionary<string, ITaxStrategy> {
{"US", kernel.Resolve<USTaxStrategy>()},
{"CA", kernel.Resolve<CanadaTaxStrategy>()},
})));
var order = new Order { Country = "US" };
container.Resolve<OrderProcessor>().Process(order);
Assert.AreEqual(10, order.Tax);
}
[Test]
public void InjectWithProxy() {
var container = new WindsorContainer();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessorInterceptor>();
container.AddComponent<ITaxStrategyFactory, TaxStrategyFactory>();
container.Register(Component.For<OrderProcessor2>()
.LifeStyle.Transient
.Interceptors(InterceptorReference.ForType<OrderProcessorInterceptor>()).First);
var order = new Order {Country = "CA"};
container.Resolve<OrderProcessor2>().Process(order);
Assert.AreEqual(5, order.Tax);
}
public class OrderProcessorInterceptor : IInterceptor {
private readonly ITaxStrategyFactory strategyFactory;
public OrderProcessorInterceptor(ITaxStrategyFactory strategyFactory) {
this.strategyFactory = strategyFactory;
}
public void Intercept(IInvocation invocation) {
if (invocation.MethodInvocationTarget.Name == "Process") {
var processor = (OrderProcessor2) invocation.InvocationTarget;
var order = (Order) invocation.Arguments[0];
processor.Strategy = strategyFactory.Create(order);
}
invocation.Proceed();
}
}
public interface IOrderProcessor {
void Process(Order order);
}
public class OrderProcessor2 : IOrderProcessor {
public ITaxStrategy Strategy { get; set; }
public virtual void Process(Order order) {
order.Tax = Strategy.CalcTax(order);
}
}
public class OrderProcessor : IOrderProcessor {
private readonly ITaxStrategyFactory strategyFactory;
public OrderProcessor(ITaxStrategyFactory strategyFactory) {
this.strategyFactory = strategyFactory;
}
public void Process(Order order) {
var strategy = strategyFactory.Create(order);
order.Tax = strategy.CalcTax(order);
}
}
public interface ITaxStrategyFactory {
ITaxStrategy Create(Order o);
}
public class TaxStrategyFactory : ITaxStrategyFactory {
private readonly IKernel kernel;
public TaxStrategyFactory(IKernel kernel) {
this.kernel = kernel;
}
public ITaxStrategy Create(Order o) {
if (o.Country == "US")
return kernel.Resolve<USTaxStrategy>();
return kernel.Resolve<CanadaTaxStrategy>();
}
}
public class TaxStrategyFactory2: ITaxStrategyFactory {
private readonly IDictionary<string, ITaxStrategy> strategies;
public TaxStrategyFactory2(IDictionary<string, ITaxStrategy> strategies) {
this.strategies = strategies;
}
public ITaxStrategy Create(Order o) {
return strategies[o.Country];
}
}
public interface ITaxStrategy {
decimal CalcTax(Order order);
}
public class USTaxStrategy : ITaxStrategy {
public decimal CalcTax(Order order) {
return 10;
}
}
public class CanadaTaxStrategy : ITaxStrategy {
public decimal CalcTax(Order order) {
return 5;
}
}
public class Order {
public string Country { get; set; }
public decimal Tax { get; set; }
}
}

Resources