How can I refactor this Dictionary to a Class? - c#

I feel this Dictionary is holding too much information: It holds information to build an
e-mail path and it holds extra parameters to get other data needed for e-mail templates. Here is a simplified version of my sample program:
void Main()
{
//Sample Path = Root/Action/TemplateX.txt
//Date used in other method
Dictionary<string,object> emailDict = new Dictionary<string,object>
{
{"Root","Email"},
{"Action", "Update"},
{"TemplateName", "TemplateX.txt"},
{"Date", DateTime.Now},
};
//Create email object
Email email = new Email();
//Send e-mail with email dictionary
email.SendEmail(emailDict);
}
// Define other methods and classes here
public class Email
{
public void SendEmail(Dictionary<string,object> emailDict)
{
//Build path from emailDict and use parameters from emailDict
//Send E-mail
}
}
Are there other re-factors I should consider?

You are certainly right - what you have needs to be refactored. Perhaps reading up on standard Object Orientated principals would help. I would have something more like this, though I would need to know more of how you plan to use it (public setters may be desirable):
enum EmailAction { Update } // add any other possible actions
public class Email
{
public string Email { get; private set; }
public EmailAction EmailAction { get; private set; }
public string TemlateName { get; private set; }
public DateTime DateTime { get; private set; }
public Email(string email, EmailAction action, string templateName, DateTime dateTime)
{
this.Email = email;
this.EmailAction = action;
this.TemlateName = templateName;
this.DateTime = dateTime;
}
public void Send()
{
//Build path from properties on this instance of Email
}
}
Then you can simply go:
Email newEmail = new Email("Email", EmailAction.Update, "TemplateX.txt", DateTime.Now);
newEmail.Send();

That is definitely abusing a Dictionary. You're losing all type safety having your value be an object which leaves you open to InvalidCast exceptions and a whole bunch of other issues. Just pull out all of your values into properties in a class:
public class EmailFields
{
public string Root {get;set;}
public string Action {get;set;}
public string TemplateName {get;set;}
public DateTime Date {get;set;}
public EmailHelper
{
Date = DateTime.Now;
}
}
Your SendEmail method would then take an EmailFields object as a parameter.
From this point, I'd also probably make enum's for Action and TemplateName.
public enum Action
{
Update,
}
public enum Template
{
TemplateX,
}
And your properties would then be
public Action EmailAction {get;set;}
public Template TemplateName {get;set;}

Related

Custom Property name for FromUrl Model

I have a model which is used to bind QueryString, that follows the naming conversation of c# but the QueryString is in a different naming conversation. How to provide a custom property name for model properties that are assigned vis FromUrl?
// Will NOT work
public class FormatDatabaseRequest
{
[JsonProperty("_type")]
public string Type { get; set; }
[JsonProperty(Name = "awef_flag")]
public string AwefFlag { get; set; }
}
// Controller.cs
[HttpPost]
public async Task<HttpResponseMessage> FormatDatabaseAsync([FromUri] FormatDatabaseRequest request) {}
// Sample URL (QueryString MUST be named _type and awef_flag)
// https://localhost:43521/myControllerName?_type=asdfa&awef_flag=asdf
If you want to get fields from URL like that, I recommend using [FromQuery] attribute, like so:
public async Task<HttpResponseMessage> Get([FromQuery] FormatDatabaseRequest data)
Then, such URL
https://localhost:43521/myControllerName?type=asdfa&awefflag=asdf
Will be parsed correctly to your object :)
json has nothing to do with query string. I don't understand why you don't like underscore properties, but you can hide them like this
public class FormatBaseRequest
{
public string _Type { get; set; }
public string Awef_flag{ get; set; }
}
public class FormatDatabaseRequest:FormatBaseRequest
{
public string Type
{
get { return _Type; }
set { _Type=value ; } //or leave just get
}
public string AwefFlag
{
get { return Awef_flag; }
set { Awef_flag=value ; } //or leave just get
}
}
you can use it for query string and for c#

what's an elegant method to transfer/cast a poco of 1 type to a poco of another type - different namespace but same name and fields?

Let's say you have a Domain layer which returns a User object:
public class User
{
public string FirstName{get;set;}
public string LastName{get;set;}
}
Let's say you have an identical class defined in your service layer. What's an elegant method to easily transfer/cast the Domain User object into a service User object?
"Elegant" is subjective. I might just write an extension that converts one to the other.
public static class MappingExtensions
{
public ThisNameSpace.User ToThisUser(this OtherNameSpace.User source)
{
return new ThisNameSpace.User
{
FirstName = source.FirstName,
LastName = source.LastName,
UserId = source.UserId
}
}
}
To me that's the simplest.
You could also use Automapper (add from Nuget.)
Do a one-time configuration:
AutoMapper.Mapper.Initialize(c=>c.CreateMap<User,Other.User>());
and then you can call it to map an instance of User to a new instance of Other.User.
var other = AutoMapper.Mapper.Map<Other.User>(user);
It works without specifying the mapping for individual properties if the property names and types are identical.
You can use reflection:
using System.Windows.Forms;
using System.Reflection;
namespace First
{
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserID { get; set; }
public User()
{
}
}
}
namespace Second
{
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserID { get; set; }
public User()
{
}
}
}
namespace YetAnotherNamespace
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
First.User user = new First.User()
{
FirstName = "John",
LastName = "Public",
UserID = "jpublic#mydomain.com"
};
Second.User newUser = ConvertUser(user);
}
public Second.User ConvertUser(First.User oldUser)
{
Second.User newUser = new Second.User();
foreach (PropertyInfo prop in oldUser.GetType().GetProperties())
{
string propertyName = prop.Name;
object propertyValue = prop.GetValue(oldUser);
newUser.GetType().GetProperty(propertyName).SetValue(newUser, propertyValue);
}
return newUser;
}
}
}
In this code sample, when I instantiate the form, I create a First.User and convert it to a Second.User.
This is tested and working (as a strongly-typed method). I think you can make it more generic and accept and return an "object", and it will just throw an exception if the properties don't match up. Also keep in mind that reflection tends to be slow - this may not be the most scalable solution.
Another approach would be serializing to json and then deserializing to the other type.

How to force to change some method when new property is added to class

I have the following issue
Here is third party class which we are used(so i cannot change it)
public class ThirdPartyEmployee
{
public string F_Name { get; set; }
public string L_Name { get; set; }
public DateTime Date_of_birth { get; set; }
public string Telephone1 { get; set; }
public string Telephone2 { get; set; }
public string Position { get; set; }
//..... and so on
}
Also we have our own smaller and better Employee class
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
}
Sometimes we need to convert third party class to our own. There is extension method for it
public static class ThirdPartyExtensions
{
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
Now about the issue. If somebody consider to add some other properties to Employee class he\she can forget to change ConvertTo method. How we can avoid it ? Ideally i would like to have some compilation errors ...
Any suggestions ?
If your Employee class is just a container, there's one simple approach:
public class Employee
{
private readonly string firstName;
public Employee(string firstName)
{
this.firstName = firstName;
}
}
Now your conversion method has no choice but to pass all the arguments, so you get a compiler error when the conversion method isn't updated.
Of course, this still isn't foolproof - if you also care about changing the arguments, this doesn't help much.
And now that we have Roslyn, with great integration in Visual Studio, you can actually make your own compiler errors using a Roslyn analyzer. If you're not afraid of getting your hands dirty, this would be a great opportunity to show how useful something like that can be. Sadly, it's not very easy to use right now, and needs "the right kind of thinking" to be wielded well. It will allow you to make rules like "a class conversion extension method must assign all properties in the resulting class", for example.
You can not create a compilation error with standard means. There may be Visual Studio plugins that allow you to do that.
But it may not be necessary: You could change the CopyTo method so that instead of hardcoding all the properties to be copied, it uses reflection to obtain a list of all public properties to copy.
Example code to start with:
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
i.SetValue(destination, fi.GetValue(source));
}
To handle different property names: You could introduce an attribute which allows you to specify which property of ThirdPartyEmployee translates to which property of Employee. This can also be evaluated using reflection.
Example:
public class Employee
{
[CopyFromThirdPartyEmployee("F_Name")]
public string FirstName { get; set; }
[CopyFromThirdPartyEmployee("L_Name")]
public string LastName { get; set; }
[CopyFromThirdPartyEmployee("Telephone1")]
public string MobileTelephone { get; set; }
}
You could have the CopyTo method throw an exception when it finds a public property which does not have the required mapping attribute. That way you could be sure that every property also has the attribute - but that would be a runtime error, not a compile time error.
Another approach be to simply make Employee a wrapper for ThirdPartyEmployee:
public class Employee
{
private ThirdPartyEmployee _baseEmployee;
public Employee() { _baseEmployee = new ThirdPartyEmployee(); }
public Employee(ThirdPartyEmployee e) { _baseEmployee = e; }
public string FirstName
{
get { return _baseEmployee.F_Name; }
set { _baseEmployee.F_Name = value; }
}
...
}
That way you'd notice that if you can't access a property you haven't implemented it. The downside is that every employee would then be based on a ThirdPartyEmployee.
You can do this with the help of reflection, but dictionary for names mapping is needed:
public static class ThirdPartyExtensions
{
static Dictionary<string, string> map;
static ThirdPartyExtensions()
{
map = new Dictionary<string, string>{ {"F_Name", "FirstName"} /*and others*/};
}
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
if(map.Count < typeof(Employee).GetProperties().Count())
throw new Exception("Forget to add mapping for new field!");
foreach(var prop in typeof(ThirdPartyEmployee).GetProperties())
if(map.ContainsKey(prop.Name))
{
var temp = typeof(Employee).GetProperty(map[prop.Name]);
temp.SetValue(result, prop.GetValue(thirdPartyEmployee));
}
return result;
}
}
Using Roslyn analyzers it's possible to produce compile-time (and IntelliSense) errors that go beyond the scope of the compiler. Here's a very simple implementation of an analyzer that checks that all the properties of a type returned by a method have been assigned. It doesn't take into account control flow (e.g. if).
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AssignAllPropertiesAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("AssignAllPropertiesAnalyzer",
"All properties must be assigned.", "All properties of the return type must be assigned.", "Correctness",
DiagnosticSeverity.Warning, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
var methodNode = (MethodDeclarationSyntax)context.Node;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode);
if (methodSymbol.GetReturnTypeAttributes().Any(x => x.AttributeClass.Name == "AssignAllPropertiesAttribute"))
{
var properties = methodSymbol.ReturnType.GetMembers().OfType<IPropertySymbol>().Where(x => !x.IsReadOnly).ToList();
foreach (var assignmentNode in methodNode.DescendantNodes().OfType<AssignmentExpressionSyntax>())
{
var propertySymbol = context.SemanticModel.GetSymbolInfo(assignmentNode.Left).Symbol as IPropertySymbol;
if (propertySymbol != null)
{
properties.Remove(propertySymbol);
}
}
if (properties.Count > 0)
{
var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0]);
context.ReportDiagnostic(diagnostic);
}
}
}
The analyzer assumes an attribute named AssignAllProperties is applied to return type of a method. In the following example, ~~~~~~ marks the location where analyzer would produce a diagnostic.
class A
{
public string S { get; set; }
}
[return: AssignAllProperties]
public static A Create()
~~~~~~
{
return new A();
}
An analyzer can be installed both as a VSIX and as a NuGet package. I would recommend always using the NuGet approach - it would apply the analyzer for everyone consuming the code and would allow you to change the severity (e.g. to error), thus failing the compilation. To get started with building an analyzer library, install the Roslyn SDK and create an Analyzer with Code Fix C# project.
You cannot generate a compile error for that, but... I would move the conversion method to the Employee class. I suggest to avoid Extension methods that are dependend on data (like properties from other classes)
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
public static Employee From(ThirdPartyEmployee employee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
This way you keep all functionality in the right class/file and it is clear for others if they add properties.

Dynamic RegularExpression attribute

I have this form where there's a Postal Code field, in my ViewModel it looks something like this:
[RegularExpression(#"^\d{5}(-\d{4})?$")]
public string PostalCode { get; set; }
That regular expression accepts 5 digits postal codes, but now I need to support other countries where they use 8, 4 or 6 digits postal codes.
I have those custom regex in a database, but I can't pass non-static variables to an attribute in this way:
[RegularExpression(MyCustomRegex)]
public string PostalCode { get; set; }
What can I do? I tried creating a custom attribute, but in some point I needed to pass a non-static parameter, which is not possible.
Should I use reflection? Is there a cleaner way?
A better way may be to decouple the attribute from the regex.
public class PostalCodeAttribute : Attribute
{
public string Country { get; set; }
}
public interface IPostalCodeModel
{
string PostalCode { get; }
}
public class UsModel : IPostalCodeModel
{
[PostalCode(Country = "en-US")]
public string PostalCode { get; set; }
}
public class GbModel : IPostalCodeModel
{
[PostalCode(Country = "en-GB")]
public string PostalCode { get; set; }
}
Validator:
public class PostalCodeValidator
{
private readonly IRegularExpressionService _regularExpressionService;
public PostalCodeValidator(IRegularExpressionService regularExpressionService)
{
_regularExpressionService = regularExpressionService;
}
public bool IsValid(IPostalCodeModel model)
{
var postalCodeProperty = model.GetType().GetProperty("PostalCode");
var attribute = postalCodeProperty.GetCustomAttribute(typeof(PostalCodeAttribute)) as PostalCodeAttribute;
// Model doesn't implement PostalCodeAttribute
if(attribute == null) return true;
return ValidatePostalCode(_regularExpressionService, model, attribute.Country);
}
private static bool ValidatePostalCode(
IRegularExpressionService regularExpressionService,
IPostalCodeModel model,
string country
)
{
var regex = regularExpressionService.GetPostalCodeRegex(country);
return Regex.IsMatch(model.PostalCode, regex);
}
}
As indicated in several related questions (e.g. Pass instance of Class as parameter to Attribute constructor Lambda expression in attribute constructor) only compile time literals are allowed as arguments for an attribute.
I did think of a workaround that may or may not work. The idea is to create a custom attribute class that derives from the regular expression attribute and that performs a regex lookup on construction and passes the result to its base.
DISCLAIMER: I haven't actually tested it (and am not planning on doing so ;-).
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class PostalCodeAttribute : RegularExpressionAttribute
{
private static ConcurrentDictionary<string, Func<string, string>> _resolverDict = new ConcurrentDictionary<string, Func<string, string>>();
private static string Resolve(string source)
{
Func<string, string> resolver = null;
if (!_resolverDict.TryGetValue(source, out resolver))
throw new InvalidOperationException(string.Format("No resolver for {0}", source));
return resolver(source);
}
public static void RegisterResolver(string source, Func<string, string> resolver)
{
_resolverDict.AddOrUpdate(source, resolver, (s, c) => resolver);
}
static PostalCodeAttribute()
{
// necessary to enable client side validation
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PostalCodeAttribute), typeof(RegularExpressionAttributeAdapter));
}
public PostalCodeAttribute(string patternSource)
: base(Resolve(patternSource))
{
}
}
/// ...
public void SomeIntializer()
{
PostalCodeAttribute.RegisterResolver("db_source", (s) => PostalCodeRegularExpressions.LookupFromDatabase());
}
public class SomeClassWithDataValidation
{
[PostalCode("db_source")]
public string PostalCode { get; set; }
}
Note that this will only work, if registration of a matching resolver function is done before any of these attributes are instantiated.

Auditing rows added to Azure table storage

I have created the following class which I believe gives me some good auditing capabilities for data rows in certain tables that require it. Here is the class I am using:
public class AuditableTableServiceEntity : TableServiceEntity
{
protected AuditableTableServiceEntity()
: base()
{
}
protected AuditableTableServiceEntity(string pk, string rk)
: base(pk, rk)
{
}
#region CreatedBy and ModifiedBy
private string _CreatedBy;
[DisplayName("Created By")]
public string CreatedBy
{
get { return _CreatedBy; }
set { _CreatedBy = value; Created = DateTime.Now; }
}
[DisplayName("Created")]
public DateTime? Created { get; set; }
private string _ModifiedBy;
[DisplayName("Modified By")]
public string ModifiedBy
{
get { return _ModifiedBy; }
set { _ModifiedBy = value; Modified = DateTime.Now; }
}
[DisplayName("Modified")]
public DateTime? Modified { get; set; }
#endregion
}
Can anyone out there suggest any additional changes that I might consider for this class. I believe it is okay but as I need to implement this for many classes I would like to hear if anyone can suggest any changes or additions.
private string _ModifiedBy;
[DisplayName("Modified By")]
public string ModifiedBy
{
get { return _ModifiedBy; }
set { _ModifiedBy = value; Modified = DateTime.Now; }
}
will cause a stack overflow: setting the value of a property in a setter calls the setter, which sets the value of the property, which calls the setter, and so on.
You could set the properties in a constructor, but then things break if an instance is serialized and deserialized: when you deserialize it, the public parameterless constructor is called, and the setter is called... which sets the property to the date and time that the object was deserialized, not the stored value.
A better pattern might be to create another table for auditable events. This might look something like this:
public class Audit
{
public string ModifiedBy { get; set; }
public DateTime DateModified { get; set; }
public Type ObjectType { get; set; }
public string Field { get; set; }
public object OldValue { get; set; }
public object NewValue { get; set; }
public static void Record(string user, Type objectType, object oldValue, object newValue)
{
Audit newEvent = new Audit
{
ModifiedBy = user,
DateModified = DateTime.UtcNow, // UtcNow avoids timezone issues
ObjectType = objectType,
OldValue = oldValue,
NewValue = newValue
};
Save(newEvent); // implement according to your particular storage classes
}
}
Then, whenever you make changes to an object you want to audit, call Audit.Record() like so:
public class SomeKindOfAuditableEntity
{
private string _importantFieldToTrack;
public string ImportantFieldToTrack
{
get { return _importantFieldToTrack; }
set
{
Audit.Record(GetCurrentUser(), this.GetType(), _importantFieldToTrack, value);
_importantFieldToTrack = value;
}
}
}
This way you store a log of all changes that happen to all "interesting" properties of your tables. This has a few other advantages:
you see the old and new values of each change
the audit log is stored in a different place from the data itself, separating concerns
you don't need to have a base class for your data classes
the audit for old changes is kept around so you can go back through the entire log of the object's changes
The principal disadvantage is that you need to add code to each setter for each property you're interested in. There are ways to mitigate this with attributes and reflection and aspect-oriented programming -- see for instance Spring's implementation here: http://www.springframework.net/doc-latest/reference/html/aop.html -- in essence, you'd create an attribute for the properties you'd like to track.
The other disadvantage is that you'll consume lots of storage for the audit log - but you can have a background process that trims down the old entries periodically as you see fit.

Categories