I have big model, that aggragates data for buisness entity.
class BigObject
{
TypeA DataA { get;set; }
TypeB DataB { get;set; }
TypeC DataC { get;set; }
}
and have service, which fill fields of model from differents sources. Some data depends from another data
class DataService
{
public BigObject GetModel()
{
var model = new BigObject();
model.DataA = sourceServiceA.GetData();
model.DataB = sourceServiceB.GetData(model.DataA.Id);
model.DataC = sourceServiceC.GetData();
}
}
In method GetModel() I need to configure, which fields should be filled, which should not. For example, I want to fill DataA property, but don't want fill others. First idea is pass in method object BigObjectFilter
public BigObject GetModel(BigObjectFilter filter)
class BigObjectFilter
{
bool FillDataA { get; set; }
bool FillDataB { get; set; }
bool FillDataC { get; set; }
}
and initialize this object in DataService clients.
In GetObject method I was going to add conditions like
if (filter.FillDataA)
{
model.DataA = sourceServiceA.GetData();
}
if (filter.FillDataC)
{
model.DataC = sourceServiceC.GetData();
}
I see, that this solution looks like bad practice. I would like to improve this construction. How can i improve it? I can't see, how to use builder pattern in this case, because i have requeired and optional data, one depends on the other.
For the sake of simplicity let's assume that TypeA, TypeB and TypeC are int?.
We can define a command class for the BigObject with the following constructors:
class BigObjectCommand
{
private readonly Func<BigObjectFilter, bool> canExecute;
private readonly Action<BigObject>? executeWithoutParam;
private readonly Action<int, BigObject>? executeWithParam;
private readonly Expression<Func<BigObject, int?>>? dependsOn;
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<BigObject> execute)
{
this.canExecute = canExecute;
this.executeWithoutParam = execute;
}
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<int, BigObject> execute, Expression<Func<BigObject, int?>> dependsOn)
{
this.canExecute = canExecute;
this.executeWithParam = execute;
this.dependsOn = dependsOn;
}
}
The first constructor will be used to cover the DataA and DataC properties' initialization
The second constructor will be used to cover the initialization of DataB property
Now, we can define an Evaluate method to implement the core logic
public void Evaluate(BigObjectFilter filter, BigObject context)
{
if (!canExecute(filter))
return; //or throw exception
if (executeWithoutParam is not null)
{
executeWithoutParam(context);
return;
}
var input = dependsOn!.Compile()(context);
if (!input.HasValue)
return; //or throw exception
executeWithParam!(input.Value, context);
}
If the condition fails we don't do the assignment
If the assignment does not require any input then we simply execute it
If the assignment depends on an input then we check whether it is populated or not and depending on the result we may or may not execute the assignment
With these in our hand the GetModel can be implemented like this:
private readonly List<BigObjectCommand> Commands;
public DataService()
{
Commands = new()
{
new (filter => filter.FillDataA, (m) => m.DataA = sourceServiceA.GetData()),
new (filter => filter.FillDataB, (i, m) => m.DataB = sourceServiceB.GetData(i), m => m.DataA),
new (filter => filter.FillDataC, (m) => m.DataC = sourceServiceC.GetData()),
};
}
public BigObject GetModel(BigObjectFilter filter)
{
var model = new BigObject();
foreach (var command in Commands)
{
command.Evaluate(filter, model);
}
return model;
}
This solution is far from perfect I just wanted to share with you the basic idea how to apply the Command pattern for your problem.
Here you can a find a working example. By changing the FillDataXYZ values you can see how the Evaluate works in practice.
It looks like you have at least two choices here:
use some collection which stores value to handle
an approach inspired by Chain-of-responsibility pattern
Let's start from collection which stores value to handle
At first, we need our class with properties to be filled:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
Then this is our class which will handle all your properties:
public class BigObjectHandler
{
Dictionary<string, Action> _handlerByproperty = new ();
BigObject _bigObject;
public BigObjectHandler(BigObject bigObject)
{
_bigObject = bigObject;
_handlerByproperty.Add("A", GetDataA);
_handlerByproperty.Add("B", GetDataB);
_handlerByproperty.Add("C", GetDataC);
}
public void Handle(string propertyName) =>
_handlerByproperty[propertyName].Invoke();
private void GetDataA()
{
_bigObject.A = 1; // sourceServiceA.GetData();
}
private void GetDataB()
{
_bigObject.B = 1; // sourceServiceA.GetData();
}
private void GetDataC()
{
_bigObject.C = 1; // sourceServiceA.GetData();
}
}
And then you can call the above code like this:
IEnumerable<string> propertiesToFill = new List<string> { "A", "B" };
BigObject bigObject = new ();
BigObjectHandler bigObjectMapHandler = new (bigObject);
foreach (var propertyToFill in propertiesToFill)
{
bigObjectMapHandler.Handle(propertyToFill);
}
OUTPUT:
A = 1
B = 1
Chain-of-responsibility pattern
If you have many if else statements, then you can try to use "Chain-of-responsibility pattern". As wiki says:
the chain-of-responsibility pattern is a behavioral design pattern
consisting of a source of command objects and a series of processing
objects. Each processing object contains logic that defines the
types of command objects that it can handle; the rest are passed to
the next processing object in the chain. A mechanism also exists for
adding new processing objects to the end of this chain
However, we will not stop execution if some of condition is met. Let me show an example.
At first, we need some abstraction of handler:
public abstract class BigObjectHandler
{
private BigObjectHandler _nextBigObjectHandler;
public void SetSuccessor(BigObjectHandler bigObjectHandler)
{
_nextBigObjectHandler = bigObjectHandler;
}
public virtual BigObject Execute(BigObject bigObject,
BigObjectFilter parameter)
{
if (_nextBigObjectHandler != null)
return _nextBigObjectHandler.Execute(bigObject, parameter);
return bigObject;
}
}
Then we need concrete implemenatation of these handlers for your properties. This properties will be filled
by your sourceServiceX.GetData():
public class BigObjectAHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillA)
{
bigObject.A = 1; // sourceServiceA.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectBHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillB)
{
bigObject.B = 2; // sourceServiceB.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectCHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillC)
{
bigObject.C = 3; // sourceServiceC.GetData();
}
return base.Execute(bigObject, filter);
}
}
And these are object with data:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
And some filter which will contain settings of what property should be filled:
public class BigObjectFilter
{
public bool FillA { get; set; } = true;
public bool FillB { get; set; }
public bool FillC { get; set; }
}
And then we can call the above code like this:
BigObjectHandler chain = new BigObjectAHandler();
BigObjectHandler objectBHandler = new BigObjectBHandler();
BigObjectHandler objectCHandler = new BigObjectCHandler();
chain.SetSuccessor(objectBHandler);
objectBHandler.SetSuccessor(objectCHandler);
BigObjectFilter bigObjectFilter = new BigObjectFilter();
bigObjectFilter.FillA = true;
BigObject vehicle = chain.Execute(new BigObject(), bigObjectFilter); // A = 1
It can be seen after code execution that onle property A is handled. Output is:
A = 1
B = 1
Related
I'm currently studying C# and I'm quiet stunned over a simple task.
I have this code to test:
public interface IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { set; }
}
public static class MedicalScheduler
{
public static Dictionary<DateTime, string> Appointments { get; set; } = new Dictionary<DateTime, string>();
public static List<DateTime> FreeSlots { get; set; } = new List<DateTime>();
public static IEnumerable<Tuple<string, bool>> Schedule(IEnumerable<IAppointment> requests)
{
bool slotFound = false;
foreach (var appointment in requests)
{
if (slotFound) continue;
foreach (var times in appointment.ProposedTimes)
{
var freeSlot = FreeSlots.Where(s => s.Date == times.Date).FirstOrDefault();
if (freeSlot != null)
{
slotFound = true;
Appointments.Remove(freeSlot);
appointment.SelectedAppointmentTime = freeSlot;
yield return new Tuple<string, bool>(appointment.PatientName, true);
}
}
yield return new Tuple<string, bool>(appointment.PatientName, false);
}
}
}
And I'm required to test "Schedule" with a certain set of parameters. For example, I need to test it with empty Appointments and FreeList but with a single element in "requests".
I think I have understood how to compile a Unit Test and to set the Dictionary and List parameters. But I'm not sure how to create the IEnumerable variable.
My idea was to create a List of IAppointment(s), but how can I implement the interface in the test unit? I have tried using Moq but I didn't understood how I should use it correctly.
I'm sorry if the request seems quite confusing, but I don't know how to explain better :)
Thanks in advance for the help.
Please see the following example:
[Test]
public void Schedule()
{
// Arrange
var appointmentMock = new Mock<IAppointment>();
appointmentMock.Setup(appointment => appointment.PatientName).Returns("Dixie Dörner");
appointmentMock.Setup(appointment => appointment.ProposedTimes).Returns(
new List<DateTime>
{
new DateTime(1953,4,12),
new DateTime(1953,4,13)
});
var requests = new List<IAppointment>{appointmentMock.Object};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
MedicalScheduler.Schedule accepts any parameter implementing IEnumerable<IAppointment>, e. g. List<IAppointment> or Collection<IAppointment>.
So you simply create a List<IAppointment> and fill it with custom instances of IAppointment.
You can use Moq for creating the instances, as I did in the example. But for my own projects, I prefer the builder pattern:
internal static class AppointmentBuilder
{
public static IAppointment CreateDefault() => new Appointment();
public static IAppointment WithPatientName(this IAppointment appointment, string patientName)
{
appointment.PatientName = patientName;
return appointment;
}
public static IAppointment WithProposedTimes(this IAppointment appointment, params DateTime[] proposedTimes)
{
appointment.ProposedTimes = proposedTimes;
return appointment;
}
private class Appointment : IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { get; set; }
}
}
[Test]
public void Schedule()
{
// Arrange
var requests = new List<IAppointment>{AppointmentBuilder.CreateDefault()
.WithPatientName("Dixie")
.WithProposedTimes(new DateTime(1953,4,12))};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
I am using Strategy Pattern, I have heaps of rules and I need to check all rows in Azure storage table against each Rule.
interface IRule where TEntity : TableEntity, new()
{
string TableName { get; } // It could be "ContractAccount", "Bill", "Transaction" etc.
string Rule { get; }
string SaveToTable { get; }
TableQuery<TEntity> TableQuery { get; }
ReportEntity Handle(TableEntity entity);
}
So instance of rules lives inside the Validator.
public Validator()
{
Rules = new List<IRule>();
Rules.Add(new AddressRule());
}
The Table Entity class(ContractAccount.cs Bill.cs etc.) will have the same name as the value IRule.TableName holds.
So this is where the ContractAccount comes from.
Then in the Validator, I have Validate() which looks like:
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = new TableQuery<ContractAccount>(); //<-- I want to replace ContractAccount with something generic
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
In my AddressRule.cs
public class AddressRule : IRule<ContractAccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public TableQuery<ContractAccount> TableQuery => new TableQuery<ContractAccount>();
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
}
As you can see
var query = new TableQuery<ContractAccount>();
I need to replace the Hard-coded with something like:
var type = Type.GetType(tableName);
var query = new TableQuery<type>();
but the placeholder(ContractAccount) will change when app is running, it could be Bill, Policy, Transaction etc....
I cannot use the <T> thing.
How can I replace the ContractAccount with a generic thing?
Update 2
After applied Juston.Another.Programmer's suggection, I got this error.
Update 3
Now I updated code to below:
interface IRule<TEntity> where TEntity : TableEntity
{
string TableName { get; }
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Which I specified what type of class the TEntity has to be, it removes the 1st error, but the 2nd error persists:
Error CS0310 'TEntity' must be a non-abstract type with a public
parameterless constructor in order to use it as parameter 'TElement'
in the generic type or method 'TableQuery'
Update 4
I found how to fix the another error:
interface IRule<TEntity>
where TEntity : TableEntity, new()
But then, I have problem to add my AddressRule into Rules in the Validator class.
public Validator()
{
Rules = new List<IRule<TableEntity>>();
var addressRule = new AddressRule();
Rules.Add(addressRule);
}
Something like this:
var genericType = typeof(TableQuery<>);
Type[] itemTypes = { Type.GetType("MyNamespace.Foo.Entities." + tableName) };
var concretType = genericType.MakeGenericType(itemTypes);
var query = Activator.CreateInstance(concretType);
You could use reflection like #Christoph suggested, but in this case there's an easier approach. Add a TEntity generic parameter to your IRule class instead of using the TableName string property and add a GetTableQuery method to the class.
interface IRule<TEntity>
{
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Then, in your IRule<TEntity> implementations add the correct entity. Eg for AddressRule.
public class AddressRule : IRule<ContractAcccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
public TableQuery<ContractAccount> GetTableQuery()
{
return new TableQuery<ContractAccount>();
}
}
Now, in your Validate method, you can use the GetTableQuery method from the IRule.
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = rule.GetTableQuery();
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
The longer I think about it the more I get the feeling that what you need is a generic solution and not one with generics. I guess that the table client in line
var tableClient = storageAccount.CreateCloudTableClient();
does always return something like a DataTable or an object with an IEnumerable, independently of whether you ask for a ContractAccount or a Bill. If that's the case, it might be better to have a validator that loads all the rules of all entities from the database (or through factory patterns or hardcoded) and then applies the according ones to the given entity.
Like that, the set of rules can be defined using XML or some other sort of serialization (not part of this example) and only a few rule classes are needed (I call them EntityValidationRule).
The parent of all rules for all entities could look like this:
public abstract class EntityValidationRule {
//Private Fields
private Validator validator;
//Constructors
public EntityValidationRule(String tableName, IEnumerable<String> affectedFields) {
TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
AffectedFields = affectedFields?.ToArray() ?? Array.Empty<String>();
}
//Public Properties
public String TableName { get; }
public String[] AffectedFields { get; }
public virtual String Description { get; protected set; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails errorDetails) {
if (record == null) throw new InvalidOperationException("Programming error in Validator.cs");
if (!Validator.IdentifyerComparer.Equals(record.Table.TableName, TableName)) throw new InvalidOperationException("Programming error in Validator.cs");
String myError = GetErrorMessageIfInvalid(record);
if (myError == null) return true;
errorDetails = CreateErrorDetails(record, myError);
return false;
}
//Protected Properties
public Validator Validator {
get {
return validator;
}
internal set {
if ((validator != null) && (!Object.ReferenceEquals(validator, value))) {
throw new InvalidOperationException("An entity validation rule can only be added to a single validator!");
}
validator = value;
}
}
//Protected Methods
protected virtual IErrorDetails CreateErrorDetails(DataRow record, String errorMessage) {
return new ErrorDetails(record, this, errorMessage);
}
protected abstract String GetErrorMessageIfInvalid(DataRow record);
}
and to stay with your example, the sample implementation for an empty text field check could look like this (having an intermediate class OneFieldRule):
public abstract class OneFieldRule : EntityValidationRule {
public OneFieldRule(String tableName, String fieldName) : base(tableName, new String[] { fieldName }) {
}
protected String FieldName => AffectedFields[0];
}
and like this:
public class TextFieldMustHaveValue : OneFieldRule {
public TextFieldMustHaveValue(String tableName, String fieldName) : base(tableName, fieldName) {
Description = $"Field {FieldName} cannot be empty!";
}
protected override String GetErrorMessageIfInvalid(DataRow record) {
if (String.IsNullOrWhiteSpace(record.Field<String>(FieldName))) {
return Description;
}
return null;
}
}
Then the central validator that works like a service to validate whatever entity needs to be validated I might implement like this:
public sealed class Validator {
//Private Fields
private Dictionary<String, List<EntityValidationRule>> ruleDict;
//Constructors
//The list of all rules we just have somehow...
public Validator(IEnumerable<EntityValidationRule> rules, StringComparer identifyerComparer) {
if (rules == null) throw new ArgumentNullException(nameof(rules));
if (identifyerComparer == null) identifyerComparer = StringComparer.OrdinalIgnoreCase;
IdentifyerComparer = identifyerComparer;
ruleDict = new Dictionary<String, List<EntityValidationRule>>(IdentifyerComparer);
foreach (EntityValidationRule myRule in rules) {
myRule.Validator = this;
List<EntityValidationRule> myRules = null;
if (ruleDict.TryGetValue(myRule.TableName, out myRules)) {
myRules.Add(myRule);
} else {
myRules = new List<EntityValidationRule> { myRule };
ruleDict.Add(myRule.TableName, myRules);
}
}
}
//Public Properties
public StringComparer IdentifyerComparer { get; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails[] errors) {
//Check whether the record is null
if (record == null) {
errors = new IErrorDetails[] { new ErrorDetails(record, null, "The given record is null!") };
return false;
}
//Loop through every check and invoke them
List<IErrorDetails> myErrors = null;
IErrorDetails myError = null;
foreach (EntityValidationRule myRule in GetRules(record.Table.TableName)) {
if (myRule.IsValid(record, ref myError)) {
if (myErrors == null) myErrors = new List<IErrorDetails>();
myErrors.Add(myError);
}
}
//Return true if there are no errors
if (myErrors == null) return true;
//Otherwise assign them as result and return false
errors = myErrors.ToArray();
return false;
}
//Private Methods
private IEnumerable<EntityValidationRule> GetRules(String tableName) {
if (ruleDict.TryGetValue(tableName, out List<EntityValidationRule> myRules)) return myRules;
return Array.Empty<EntityValidationRule>();
}
}
And the error details as an interface:
public interface IErrorDetails {
DataRow Entity { get; }
EntityValidationRule Rule { get; }
String ErrorMessage { get; }
}
...and an implementation of it:
public class ErrorDetails : IErrorDetails {
public ErrorDetails(DataRow entity, EntityValidationRule rule, String errorMessage) {
Entity = entity;
Rule = rule;
ErrorMessage = errorMessage;
}
public DataRow Entity { get; }
public EntityValidationRule Rule { get; }
public String ErrorMessage { get; }
}
I know this is a totally different approach as you started off, but I think the generics give you a hell of a lot of work with customized entities that have customized validators for each and every table in your database. And as soon as you add a table, code needs to be written, compiled and redistributed.
I would like to build a helper method which will take property as object to anonymous method. This is just dummy code example to visualize problem not confront real solution which is way more complex and is not subject of this question.
Some reference code:
public class FooClass : SomeBaseClass {
public string StringProperty { get; set; }
public int IntProperty { get; set; }
public DateTime DateTimeProperty { get; set; }
public Object ComplexObjectProperty { get; set; }
public FooClass() {
this.FooMethod(this.StringProperty);
this.FooMethod(this.IntProperty);
this.FooMethod(this.DateTimeProperty);
this.FooMethod(this.ComplexObjectProperty);
}
public void FooMethod<T>(T obj) {
Func<bool> validateMethod = () => {
if(obj is string) return string.IsNullOrEmpty(obj.ToString());
return obj != null;
};
this.ValidateMethodsAggregate.Add(validateMethod);
}
}
public class SomeBaseClass {
protected IList<Func<bool>> ValidateMethodsAggregate = new List<Func<bool>>();
public void ValidateAll() {
foreach (var validateMethod in this.ValidateMethodsAggregate) {
var result = validateMethod();
// has errors and so on handling...
}
}
}
// Some simple code to show use case.
var foo = new FooClass();
foo.StringProperty = "new value";
foo.IntProperty = 123;
foo.ValidateAll(); // this will use "" , 0 instead of new values.
Use a function and a conditional operator with a private backing method
public FooClass()
{
this.FooMethod(() => StringProperty); // <- pass an accessor
}
public Func<bool> validateMethod;
private void FooMethod<T>(Func<T> obj)
{
//validate method
validateMethod = () => string.IsNullOrEmpty(obj()?.ToString());
}
The usage would be
var y = new FooClass();
var resTrue = y.validateMethod();
y.StringProperty = "Hi";
var resFalse = y.validateMethod();
I have a multilingual database, which returns values based on a key and an enum Language. When I convert a DB object to a model, I want the model to contain the translated value based on the key and the current language.
The key comes from the DB object but how can I pass the current language to the the Mapper.Map() function?
Currently, I am using a [ThreadStatic] attribute to set the culture before calling Mapper.Map<>, and to retrieve it in the TypeConverter.
public enum Language
{
English, French, Italian, Maltese
}
public class MultilingualValue<T>
{
public Dictionary<Language, T> Value { get; set; }
public MultilingualValue()
{
this.Value = new Dictionary<Language, T>();
}
}
public class PersonData
{
public string FirstName { get; set; }
public MultilingualValue<string> City { get; set; }
}
public void MapPerson()
{
PersonData personData = new PersonData();
personData.FirstName = "John";
personData.City = new MultilingualValue<string>();
personData.City.Value[ Language.English] = "The Zurrieq";
personData.City.Value[Language.French] = "Le Zurrieque";
MultilingualValueData.CurrentLanguage = Language.English;
var personModel = Mapper.Map<PersonData, PersonModel>(personData);
}
public class MultilingualValueToBasicDataTypeConverter<T> : ITypeConverter<MultilingualValue<T>, T>
{
public T Convert(ResolutionContext context)
{
var currentLanguage = MultilingualValueData.CurrentLanguage; //THIS IS THE [ThreadStatic] VARIABLE
if (currentLanguage == null) throw new InvalidOperationException("Please make sure to fill in CurrentLanguage");
MultilingualValue<T> sourceMultilingualValue = (MultilingualValue < T > )context.SourceValue;
T destinationValue = default(T);
if (sourceMultilingualValue != null)
{
destinationValue = sourceMultilingualValue.Value[currentLanguage.Value];
}
return destinationValue;
}
}
public static class MultilingualValueData
{
[ThreadStatic]
public static Language? CurrentLanguage;
}
I left out the configurations as I think they're unneccessary for this example. If you need them, I'll post them as well.
While this works, I find this workaround quite ugly. Is there any way to pass data through the ResolutionContext?
Just use the Map overload that takes a Action<IMappingOperationOptions>. You can add configuration elements to the Items property that are then passed to your ITypeConverter
public class CustomConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
return "translated in " + context.Options.Items["language"];
}
}
internal class Program
{
private static void Main(string[] args)
{
AutoMapper.Mapper.CreateMap<string, string>().ConvertUsing<CustomConverter>();
var result = AutoMapper.Mapper.Map<string, string>("value" , opt => opt.Items["language"] = "english");
Console.Write(result); // prints "translated in english"
Console.ReadLine();
}
}
I have the following C# code. Here the validations are kept outside the class to satisfy Open – Closed Principle. This is working fine. But the challenge is – the validations are not generic. It is specific to employee class (E.g DateOfBirthRuleForEmployee). How do I make the validations generic for all objects (DateOfBirthRuleForAnyObject).
Note: Make Generic <==> Make Type-Independent
Note: I have NameLengthRuleForEmployee validation also. New validation may come in future.
EDIT
Generic Method Example: Using “OfType” in LINQ
CODE
class Program
{
static void Main(string[] args)
{
Employee employee = new Employee();
employee.DateOfBirth = DateTime.Now;
employee.Name = "Lijo";
DateOfBirthRuleForEmployee dobRule = new
DateOfBirthRuleForEmployee();
NameLengthRuleForEmployee nameRule = new
NameLengthRuleForEmployee();
EmployeeManager employeeManager = new EmployeeManager();
employeeManager.AddRules(dobRule);
employeeManager.AddRules(nameRule);
bool result = employeeManager.validateEntity(employee);
Console.WriteLine(result);
Console.ReadLine();
}
}
public interface IEntity
{
}
public interface IRule<TEntity>
{
bool IsValid(TEntity entity);
}
public class DateOfBirthRuleForEmployee : IRule<Employee>
{
public bool IsValid(Employee entity)
{
return (entity.DateOfBirth.Year <= 1975);
}
}
public class NameLengthRuleForEmployee : IRule<Employee>
{
public bool IsValid(Employee employee)
{
return (employee.Name.Length < 5);
}
}
public class Employee : IEntity
{
private DateTime dateOfBirth;
private string name;
public DateTime DateOfBirth
{
get
{
return dateOfBirth;
}
set
{
dateOfBirth = value;
}
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
}
public class EmployeeManager
{
RulesEngine<Employee> engine = new RulesEngine<Employee>();
public void AddRules(IRule<Employee> rule)
{
engine.AddRules(rule);
//engine.AddRules(new NameLengthRuleForEmployee());
}
public bool validateEntity(Employee employee)
{
List<IRule<Employee>> rulesList = engine.GetRulesList();
//No need for type checking. Overcame Invariance problem
bool status = true;
foreach (IRule<Employee> theRule in rulesList)
{
if (!theRule.IsValid(employee))
{
status = false;
break;
}
}
return status;
}
}
public class RulesEngine<TEntity> where TEntity : IEntity
{
private List<IRule<TEntity>> ruleList = new
List<IRule<TEntity>>();
public void AddRules(IRule<TEntity> rule)
{
//invariance is the key term
ruleList.Add(rule);
}
public List<IRule<TEntity>> GetRulesList()
{
return ruleList;
}
}
The challange is for your rules to know which property of what type to validate. You can either provide this by implementing an interface that provides just that as suggested by SLaks or by quessing it dynamically or by providing a concrete rule class with a bit more information on how to access the given property, e.g.:
class NameRule<T> : IRule<T>
{
private Func<T, string> _nameAccessor;
public NameRule(Func<T, string> nameAccessor)
{
_nameAccessor = nameAccessor;
}
public bool IsValid(T instance)
{
return _nameAccessor(instance).Length > 10;
}
}
this ofcourse can be used in the following way:
NameRule<Employee> employeeNameRule = new NameRule<Employee>(x => x.name);
employeeManager.addRule(employeeNameRule);