AutoMapper with Dataset as parameter in profile - c#

I have some problem when using AutoMapper. I searched everywhere I could but either I don't understand the solution (when no code is provided) or it doesn't apply to my situation.
I have my profile :
public class MyCustomProfile : Profile
{
public MyCustomProfile()
{
CreateMap<DTO.Person, MyDataSet.PersonRow>();
}
}
And I have my method :
public static void TestAutoMapper(DTO.Person p)
{
if (Mapper.Instance == null)
{
Mapper.Initialize(cfg => cfg.AddProfile<MyCustomProfile>());
}
MyDataSet.PersonRow pr = Mapper.Map<MyDataSet.PersonRow>(p);
}
But my problem is :
I need to add in my profile .ConstructUsing(p => dts.get_XYdataset().Person.NewPersonRow()); where dts is an instance of MyDataSet.
And I also need this instance of MyDataSet dts in the method TestAutoMapper(DTO.Person p) to save the result MyDataSet.PersonRow pr as the following :
dts.get_XYdataset().Person.AddPersonRow(pr)
But I don't know what to do. It works well if I put everything in the TestAutoMapper() method but of course it's not clean and I want to separate logics by creating a profile and calling it when initializing the mapper.
EDIT
So I modified my TestAutoMapper() method like this :
public static void TestAutoMapper(DTO.Person p)
{
if (Mapper.Instance == null)
{
Mapper.Initialize(cfg => cfg.AddProfile<MyCustomProfile>());
}
using (MyDataSet dts = new MyDataSet())
{
MyDataSet.PersonRow pr = Mapper.Map<MyDataset.PersonRow>(p, opt => opt.Items["Dataset"] = dts);
dts.get_XYdataset().Person.AddPersonRow(pr);
}
Then I tried to follow the mini tuto about Custom Resolvers and implemented this class :
public class CustomResolver: IMemberValueResolver<object, object, MyDataSet, MyDataSet>
{
public MyDataSet Resolve(object source, object destination, MyDataSet dts, MyDataSet dts2, ResolutionContext context)
{
if (dts != null)
{
return dts;
}
else if (dts2 != null)
{
return dts2;
}
return new MyDataSet();
}
}
But I don't think this is okay. But well, nevermind I tried anyway. But now I'm stuck in my profile constructor in the CreateMap<DTO.Person, MyDataSet.PersonRow>() statement. How to get the Options.Items["Dataset"] in the .ConstructUsing() ?
Examples show about a CustomResolver about a member but how about specifying a constructor ?
It would be so perfect if I could do something like :
CreateMap<DTO.Person, MyDataSet.PersonRow>()
.ConstructUsing(Options.Items["Dataset"].get_XYdataset().Person.NewPersonRow());
I know my need for help can be silly but I really don't understand even by reading the docs.
What do you think ?
Thanks,
Hellcat8

Okay finally it works with this code :
public static class PersonManager
{
private static MapperConfiguration _config;
private static IMapper _mapper;
public static void TestAutoMapper(DTO.Person p)
{
if (_config == null)
{
_config = new MapperConfiguration(cfg => cfg.AddProfile<MyCustomProfile>());
}
if (_mapper == null)
{
_mapper = _config.CreateMapper();
}
using (MyDataSet dts = new MyDataSet())
{
XYdataset.PersonRow pr = _mapper.Map<XYdataset.PersonRow>(p, opt => opt.Items["Dataset"] = dts);
dts.get_XYdataset().Person.AddPersonRow(pr);
}
}
}
And I have my profile :
public class MyCustomProfile : Profile
{
public MyCustomProfile()
{
CreateMap<DTO.Person, XYdataset.PersonRow>()
.ConstructUsing((source, resolutionContext) =>
(resolutionContext.Items["Dataset"] as MyDataSet)
.(get_XYdataset().Person.NewPersonRow());
}
}

Related

Is there a way of centralizing the access to a DataContext in C# using DI pattern?

this is what I got so far, but have to keep passing the DataClassesDataContext around. I wonder if there is a better more centralized way of using the DataClassesDataContext and fill the connectionstring each time the data context is used... Thanks for all the help in advance
public interface ICustomerDataAccess
{
string GetCustomerName(int customerId);
}
public class CustomerDataAccess : ICustomerDataAccess
{
private readonly DataClassesDataContext _context;
public CustomerDataAccess(DataClassesDataContext ctx)
{
_context = ctx;
}
public string GetCustomerName(int id)
{
return _context.Customers.Where(i => i.id == id).FirstOrDefault().name;
}
}
public class DataAccessFactory
{
public static ICustomerDataAccess GetCustomerDataAccessObj(DataClassesDataContext ctx)
{
return new CustomerDataAccess(ctx);
}
}
public class CustomerService
{
CustomerBusinessLogic _customerBL;
public CustomerService(DataClassesDataContext ctx)
{
_customerBL = new CustomerBusinessLogic(new CustomerDataAccess(ctx));
}
public string GetCustomerName(int id, DataClasses1DataContext ctx)
{
return _customerBL.GetCustomerName(id,ctx);
}
}
public class CustomerBusinessLogic
{
ICustomerDataAccess _custDataAccess;
public CustomerBusinessLogic(ICustomerDataAccess custDataAccess)
{
_custDataAccess = custDataAccess;
}
public CustomerBusinessLogic(DataClassesDataContext ctx)
{
_custDataAccess = new CustomerDataAccess(ctx);
}
public string GetCustomerName(int id, DataClassesDataContext ctx)
{
_custDataAccess = DataAccessFactory.GetCustomerDataAccessObj(ctx);
return _custDataAccess.GetCustomerName(id);
}
}
// and using a code like this on the interface
private void button5_Click(object sender, EventArgs e)
{
using (var ctx = new DataClassesDataContext)
{
CustomerService customerSrv = new CustomerService(ctx);
textBox1.Text = customerSrv.GetCustomerName(1, ctx);
}
}
You can use Generic Repository with Dependency Injection. This is a bit complex structure for the first time but this can solve your problem for your problem.
Also I share with you a nice and detail example. That was created by me
https://github.com/EyupCanARSLAN/N-Tier-Architecture-with-Generic-Repository--Dependency-Injection-And-Ninject/security/dependabot
Also, an article about this topic
https://dotnettutorials.net/lesson/generic-repository-pattern-csharp-mvc/

NSubstitute and DbContext.Set<TEntity> Could not find a call to return from

I am trying to write some tests for an existing service we have. It uses the DbContext (In our case, named DatabaseContext) and the constructor looks like this:
public GenericOrderProvider(DatabaseContext context, IOrderHandler<T> orderHandler)
{
_orderHandler = orderHandler;
_context = context;
_dbSet = context.Set<T>();
}
As you can see, it's generic and sets the _dbSet when it's initialized.
I have this very simple method:
public Attempt<IQueryable<T>> List(params string[] includes)
{
var x = _dbSet.ToList();
return Attempt<IQueryable<T>>.Succeed(_dbSet.OrderBy(m => m.Order));
}
And I wrote this test:
[TestFixture]
public class ListShould
{
[Test]
public void ReturnList()
{
// Assemble
var services = GenericOrderProviderContext.GivenServices();
var provider = services.WhenCreateOrderProvider();
services.DatabaseContext.Set<Attribute>().ReturnsForAnyArgs(new List<Attribute>().ToDbSet());
//services.DatabaseContext.Attributes = new List<Attribute>().ToDbSet();
// Act
var result = provider.List();
// Assert
result.Failure.Should().BeFalse();
result.Result.Count().Should().Be(0);
}
}
When I run that test, I get the error:
NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from.
The trace specifically targets the line services.DatabaseContext.Set<Attribute>().ReturnsForAnyArgs(new List<Attribute>().ToDbSet()); but I have no idea how to fix it.
As far as I can tell, I am mapping to the right method.
For completeness, here is my test Contexts:
public class GenericOrderProviderContext: DatabaseContextContext<GenericOrderProviderContext>
{
public static GenericOrderProviderContext GivenServices() => new GenericOrderProviderContext();
public IGenericOrderProvider<Attribute> WhenCreateOrderProvider() =>
new GenericOrderProvider<Attribute>(DatabaseContext,
new OrderHandler<Attribute>(DatabaseContext));
public bool IsSequential(List<Attribute> models)
{
return !models.OrderBy(m => m.Order).Select(m => m.Order).Select((i, j) => i - j).Distinct().Skip(1).Any();
}
}
public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
public DatabaseContext DatabaseContext;
protected DatabaseContextContext()
{
DatabaseContext = Substitute.For<DatabaseContext>();
}
}
Does anyone know what I can do to resolve this issue?

how to perform a runtime view update when using ResourceViewLocationProvider

Im runing a nancyfx with owin on centos 6.5 with mono 5.10.0.140, I change the default ViewLocationProvider to ResourceViewLocationProvider for the default ViewLocationProvider causes memory leak of somekind after running for days, and the ResourceViewLocationProvider dont have the same problem. I would like to hot update Views just like what we can do with a default ViewLocationProvider, but it seems impossibe when googling around.
I did find a partial solution though, by implenting a custom IViewLocator and a IViewCache, I did achieve someking of hot update. But It didn`t feel right aside from those ugly static class
//Here is what I did in the custom IViewLocator
//...class definition fallback viewlocator and other staffs
private static ConcurrentDictionary<string, ViewLocationResult> _cachedViewLocationResults;
//..other code
public ViewLocationResult LocateView(string viewName, NancyContext context)
{
//...lock and others
if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
{
return _cachedViewLocationResults[viewName];
}
//...lock and others
return fallbackViewLocator.LocateView(viewName, context);
}
//...other class
//here is how I update Views
public static void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
{
lock (CacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new ConcurrentDictionary<string, ViewLocationResult>();
foreach (var replace in replacements)
{
_cachedViewLocationResults.AddOrUpdate(replace.Key, x=>replacements[x], (x,y)=>y);
}
}
}
//END OF IViewLocator
//here is what I did in the custom IViewCache
//another static for ViewCache to tell if the view has been updated
public static List<ViewLocationResult> Exceptions { get; private set; }
//...some other code
//here is how I ignore the old cache
public TCompiledView GetOrAdd<TCompiledView>(ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
if (Exceptions.Any(x=>x.Name == viewLocationResult.Name && x.Location == viewLocationResult.Location && x.Extension == viewLocationResult.Extension))
{
object old;
this.cache.TryRemove(viewLocationResult, out old);
Exceptions.Remove(viewLocationResult);
}
return (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x));
}
With those implentions and a little bit of settings on the bootstrapper plus a router for some mysql update, I can update the View the way I want, but here is the problem:
1. now I have to manually map all the Location,Name,Extension for the ViewLocationResult to use and there are too many of them (243...), I would like to use the some sort of built-in function to identify the changes, something like the IsStale function of the ViewLocationResult, but I didnt know which and how...
2. those static class are ugly and I think it could be problematic but I didnt know a better way to replace them.
Could some one kindly give me a hint, thank in advance.
Well, I finally figure out how to do this myself, just in case anyone else want to use the same method as I do, Here is how you update your view in memory:
Make a interface
public interface INewViewLocationResultProvider
{
bool UseCachedView { get; set; }
ViewLocationResult GetNewerVersion(string viewName, NancyContext context);
void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements);
}
Make a new ViewLocationResultProvider
public class ConcurrentNewViewLocationResultProvider : INewViewLocationResultProvider
{
private Dictionary<string, ViewLocationResult> _cachedViewLocationResults;
private readonly object _cacheLock = new object();
public bool UseCachedView { get; set; }
public ConcurrentNewViewLocationResultProvider()
{
lock (_cacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
}
}
public ViewLocationResult GetNewerVersion(string viewName, NancyContext context)
{
if (UseCachedView)
{
if (Monitor.TryEnter(_cacheLock, TimeSpan.FromMilliseconds(20)))
{
try
{
if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
{
return _cachedViewLocationResults[viewName];
}
}
finally
{
Monitor.Exit(_cacheLock);
}
}
}
return null;
}
public void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
{
lock (_cacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
foreach (var replace in replacements)
{
if (_cachedViewLocationResults.ContainsKey(replace.Key))
{
_cachedViewLocationResults[replace.Key] = replace.Value;
}
else
{
_cachedViewLocationResults.Add(replace.Key,replace.Value);
}
}
}
}
}
In your Bootstrapper,register the new ViewLocationResultProvider with tinyIoc or equivalent
container.Register<INewViewLocationResultProvider, ConcurrentNewViewLocationResultProvider>().AsSingleton();
Make a derived class from ViewLocationResult
public class OneTimeUsedViewLocationResult : ViewLocationResult
{
private bool _used = false;
public OneTimeUsedViewLocationResult(string location, string name, string extension, Func<TextReader> contents)
: base(location, name, extension, contents)
{
}
public override bool IsStale()
{
if (_used) return false;
_used = true;
return true;
}
}
And a new IViewLocator:
public class CachedViewLocator : IViewLocator
{
private readonly INewViewLocationResultProvider _newVersion;
private readonly DefaultViewLocator _fallbackViewLocator;
public CachedViewLocator(IViewLocationProvider viewLocationProvider, IEnumerable<IViewEngine> viewEngines, INewViewLocationResultProvider newVersion)
{
_fallbackViewLocator = new DefaultViewLocator(viewLocationProvider, viewEngines);
_newVersion = newVersion;
}
public ViewLocationResult LocateView(string viewName, NancyContext context)
{
if (_newVersion.UseCachedView)
{
var result = _newVersion.GetNewerVersion(viewName, context);
if (result != null) return result;
}
return _fallbackViewLocator.LocateView(viewName, context);
}
public IEnumerable<ViewLocationResult> GetAllCurrentlyDiscoveredViews()
{
return _fallbackViewLocator.GetAllCurrentlyDiscoveredViews();
}
}
}
Tell nancy about the new ViewLocator
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration.WithOverrides
(
nic =>
{
nic.ViewLocationProvider = typeof(ResourceViewLocationProvider);//use this or your equivalent
nic.ViewLocator = typeof(CachedViewLocator);
}
);
}
}
Then you can update it through a API like this:
public class YourModule : NancyModule
{
public YourModule(INewViewLocationResultProvider provider)
{
Get["/yourupdateinterface"] = param =>
{
if(!provider.UseCachedView) return HttpStatusCode.BadRequest;//in case you turn off the hot update
//you can serialize your OneTimeUsedViewLocationResult with Newtonsoft.Json and store those views in any database, like mysql, redis, and load them here
//data mock up
TextReader tr = new StringReader(Resources.TextMain);
var vlr = new OneTimeUsedViewLocationResult("","index","cshtml",()=>tr);
var dir = new Dictionary<string, ViewLocationResult> {{"index",vlr}};
//mock up ends
provider.UpdateCachedView(dir);
return HttpStatusCode.OK;
}
}
}
Note: Those code above doesn't solve the manually map all the Location,Name,Extension for the ViewLocationResult thing menthions in my question, but since I endup build a view editor for my colleges to upload their views, I don't need to solve it anymore.

Structuremap interception for registry scanned types

I have a ASP MVC 4 app that uses Structuremap. I'm trying to add logging to my application via Structuremap interception.
In a Registry, I scan a specific assembly in order to register all of it's types with the default convention:
public class ServicesRegistry : Registry
{
public ServicesRegistry()
{
Scan(x =>
{
x.AssemblyContainingType<MyMarkerService>();
x.WithDefaultConventions();
});
}
}
The interceptor:
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var watch = Stopwatch.StartNew();
invocation.Proceed();
watch.Stop();//log the time
}
}
I can add the interceptor for one specific plugin type like this:
var proxyGenerator = new ProxyGenerator();
container.Configure(x => x.For<IServiceA>().Use<ServiceA>().DecorateWith(instance => proxyGenerator.CreateInterfaceProxyWithTarget(instance, new LogInterceptor())));
but I want to make structuremap create logging proxies for all the types that were scanned in the registry.
Is there a way to achieve this?
It doesn't look like there's an easy extension point for this, but I got it working with a fairly decent solution using a custom convention. In order to help you understand the decisions I made I'll walk you through a few steps (skipping the many, many missteps I made on my way).
First lets look at the DefaultConvention which you are already using.
DefaultConvention:
public class DefaultConventionScanner : ConfigurableRegistrationConvention
{
public override void Process(Type type, Registry registry)
{
if (!TypeExtensions.IsConcrete(type))
return;
Type pluginType = this.FindPluginType(type);
if (pluginType == null || !TypeExtensions.HasConstructors(type))
return;
registry.AddType(pluginType, type);
this.ConfigureFamily(registry.For(pluginType, (ILifecycle)null));
}
public virtual Type FindPluginType(Type concreteType)
{
string interfaceName = "I" + concreteType.Name;
return Enumerable.FirstOrDefault<Type>((IEnumerable<Type>)concreteType.GetInterfaces(), (Func<Type, bool>)(t => t.Name == interfaceName));
}
}
Pretty simple, we get the type and interface pairs and check to make sure they have a constructor, if they do we register them. It would be nice to just modify this so that it calls DecorateWith, but you can only call that on For<>().Use<>(), not For().Use().
Next lets look at what DecorateWith does:
public T DecorateWith(Expression<Func<TPluginType, TPluginType>> handler)
{
this.AddInterceptor((IInterceptor) new FuncInterceptor<TPluginType>(handler, (string) null));
return this.thisInstance;
}
So this creates a FuncInterceptor and registers it. I spent a fair bit of time trying to create one of these dynamically with reflection before deciding it would just be easier to make a new class:
public class ProxyFuncInterceptor<T> : FuncInterceptor<T> where T : class
{
public ProxyFuncInterceptor() : base(x => MakeProxy(x), "")
{
}
protected ProxyFuncInterceptor(Expression<Func<T, T>> expression, string description = null)
: base(expression, description)
{
}
protected ProxyFuncInterceptor(Expression<Func<IContext, T, T>> expression, string description = null)
: base(expression, description)
{
}
private static T MakeProxy(T instance)
{
var proxyGenerator = new ProxyGenerator();
return proxyGenerator.CreateInterfaceProxyWithTarget(instance, new LogInterceptor());
}
}
This class just makes it easier to work with when we have the type as a variable.
Finally I've made my own Convention based on the Default convention.
public class DefaultConventionWithProxyScanner : ConfigurableRegistrationConvention
{
public override void Process(Type type, Registry registry)
{
if (!type.IsConcrete())
return;
var pluginType = this.FindPluginType(type);
if (pluginType == null || !type.HasConstructors())
return;
registry.AddType(pluginType, type);
var policy = CreatePolicy(pluginType);
registry.Policies.Interceptors(policy);
ConfigureFamily(registry.For(pluginType));
}
public virtual Type FindPluginType(Type concreteType)
{
var interfaceName = "I" + concreteType.Name;
return concreteType.GetInterfaces().FirstOrDefault(t => t.Name == interfaceName);
}
public IInterceptorPolicy CreatePolicy(Type pluginType)
{
var genericPolicyType = typeof(InterceptorPolicy<>);
var policyType = genericPolicyType.MakeGenericType(pluginType);
return (IInterceptorPolicy)Activator.CreateInstance(policyType, new object[]{CreateInterceptor(pluginType), null});
}
public IInterceptor CreateInterceptor(Type pluginType)
{
var genericInterceptorType = typeof(ProxyFuncInterceptor<>);
var specificInterceptor = genericInterceptorType.MakeGenericType(pluginType);
return (IInterceptor)Activator.CreateInstance(specificInterceptor);
}
}
Its almost exactly the same with one addition, I create an interceptor and interceptorType for each type we register. I then register that policy.
Finally, a few unit tests to prove it works:
[TestFixture]
public class Try4
{
[Test]
public void Can_create_interceptor()
{
var type = typeof (IServiceA);
Assert.NotNull(new DefaultConventionWithProxyScanner().CreateInterceptor(type));
}
[Test]
public void Can_create_policy()
{
var type = typeof (IServiceA);
Assert.NotNull(new DefaultConventionWithProxyScanner().CreatePolicy(type));
}
[Test]
public void Can_register_normally()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.WithDefaultConventions();
}));
var serviceA = container.GetInstance<IServiceA>();
Assert.IsFalse(ProxyUtil.IsProxy(serviceA));
Console.WriteLine(serviceA.GetType());
}
[Test]
public void Can_register_proxy_for_all()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.Convention<DefaultConventionWithProxyScanner>();
}));
var serviceA = container.GetInstance<IServiceA>();
Assert.IsTrue(ProxyUtil.IsProxy(serviceA));
Console.WriteLine(serviceA.GetType());
}
[Test]
public void Make_sure_I_wait()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.Convention<DefaultConventionWithProxyScanner>();
}));
var serviceA = container.GetInstance<IServiceA>();
serviceA.Wait();
}
}
}
public interface IServiceA
{
void Wait();
}
public class ServiceA : IServiceA
{
public void Wait()
{
Thread.Sleep(1000);
}
}
public interface IServiceB
{
}
public class ServiceB : IServiceB
{
}
There's definitely room for some clean up here (caching, make it DRY, more tests, make it easier to configure) but it works for what you need and is a pretty reasonable way of doing it.
Please ask if you have any other questions about it.

Implementing conditional in a fluent interface

I've been trying to implement a fluent interface for a set of rules in my system. What I am trying to accomplish is this
TicketRules
.RequireValidation()
.When(quartType => quartType == QuartType.Before).TotalMilageIs(64)
.When(quartType => quartType == QuartType.After).TotalMilageIs(128);
However, I have trouble implementing the When conditional how I intended to be. Currently, I need to call When() twice like in this snippet:
rules.When(param => param.Remarque == "Test").TotalMilageIs(100);
rules.When(param => param.Remarque == "Other").TotalMilageIs(50);
var params1 = new AddTicketParameters() { Remarque = "Test" };
var params2 = new AddTicketParameters() { Remarque = "Other" };
rules.ExecuteWith(params1);
Assert.That(ticket.TotalMilage, Is.EqualTo(100));
rules.ExecuteWith(params2);
Assert.That(ticket.TotalMilage, Is.EqualTo(50));
My TicketRules class looks this:
[EditorBrowsable(EditorBrowsableState.Never)]
public class TicketRules : ITicketRule, IHideObjectMembers
{
private Ticket theTicket;
public Ticket Ticket
{
set
{
theTicket = value;
}
}
private List<ITicketRule> allRules = new List<ITicketRule>();
public TicketRules()
{
}
public TicketRules(Ticket ticket)
{
theTicket = ticket;
}
public void Execute()
{
ExecuteWith(null, null);
}
public void ExecuteWith(AddTicketParameters param)
{
ExecuteWith(param, null);
}
public virtual void ExecuteWith(AddTicketParameters param, Ticket outsideTicket)
{
foreach (ITicketRule rule in allRules)
{
rule.ExecuteWith(param, theTicket ?? outsideTicket);
}
}
public TicketRules RequireValidation()
{
CreateModifierRule(ticket => ticket.NeedValidation = true);
return this;
}
public TicketRules TotalMilageIs(int milage)
{
CreateModifierRule(ticket => ticket.TotalMilage = milage);
return this;
}
private void CreateModifierRule(Action<Ticket> function)
{
AddRule(new ModifierTicketRule(function));
}
internal void AddRule(ITicketRule rule)
{
allRules.Add(rule);
}
public WhenClauseTicketRule When(Predicate<AddTicketParameters> predicate)
{
WhenClauseTicketRule whenClause = new WhenClauseTicketRule();
whenClause.Predicate = predicate;
AddRule(whenClause);
return whenClause;
}
public TicketRules UseStandardFormulaForTotalMilageAndTime()
{
AddRule(new StandardFormulaTicketRule());
return this;
}
public TicketRules EnsureMinimumMilageIs(int milage)
{
AddRule(new EnsureMinimumMilageTicketRule(milage));
return this;
}
}
the ITicketRules
internal interface ITicketRule : IHideObjectMembers
{
void ExecuteWith(AddTicketParameters param, Ticket ticket);
}
I also need to support the subclasses of AddTicketParameters in the When clause (I've though maybe using generics for that part). I'm posting here because I'm all confused in my design and the Martin Fowler articles confuse me even more.
This is known as the finishing problem when method chaining
Try this
TicketRules
.RequireValidation()
.When(quartType => quartType == QuartType.Before,
rule => rule.TotalMilageIs(64))
.When(quartType => quartType == QuartType.After,
rule => rule.TotalMilageIs(128));
It looks a little odd at first, but it wraps your conditionals into a different scope so you can conditionally execute them. Think about it like creating your own if block. By closing it, you know when you can "finish" a sub statement.

Categories