Dynamically find used properties before using them

I am looking for optimization of a template that I use for an application with dynamic forms.

I have a repository class using a method:

public Entity Find(string objectId, List<string> includedProperties); 

Returns an Entity object with the fields specified in includeProperties, since creating an entire object for all purposes is unnecessary overhead in this case (some objects have hundreds of properties).

An example domain code using this repository often looks something like this:

 var includedProperties = new List<string> { "FirstChildName" , "FirstChildDob", "SecondChildName", "SecondChildDob" } 

Then I retrieve the object:

 var person = repository.Find("123",includedProperties); 

Then I use the properties with the GetProperty(string propertyName) method:

 var firstChildDob = person.GetProperty("FirstChildDob").AsDateTime(); ...etc 

All this works great and goes well with the dynamic design of the application. However, I find it annoying that I always need to declare the list of "used" properties separately, before receiving the object.

So my question is that through reflection or some other cleverness, can I simplify the creation of "Enabled Properties" by looking at which parameters are passed later in the code using the GetProperty method?

Using the above example, I would like to create a list using such an assistant (or the like):

 var includedProperties = HelperObject.GetFieldsUsedInCurrentCodeFile(); 

This will somehow perceive which string constants were passed to the GetProperty () method, while retaining the need for an explicit declaration. Any suggestions are welcome!

+6
source share
2 answers

I really had a similar problem a while ago; the best thing that I could think of at that time was to define an enumeration containing the names of the properties that I wanted to use in the method.

Using this approach, you can create a list of included properties by cycling through an enumeration.

There are several advantages to this approach against strings:

  • Any spelling problems or property name changes are done in one place.

  • If you use a tool like Resharper, you can determine when you use unused "properties" in an enumeration.

For instance:

  private enum PersonMethodProperties { FirstChildName, FirstChildDob, SecondChildName, SecondChildDob } private void PersonMethod() { var includedProperties = GetIncludePropertiesFromEnum(typeof(PersonMethodProperties)); var person = repository.Find("123", includedProperties); var firstChildDob = person.GetProperty(PersonMethodProperties.FirstChildDob.ToString()).AsDateTime(); } private List<string> GetIncludePropertiesFromEnum(Type propertiesEnumType) { var includedProperties = new List<string>(); foreach (var name in Enum.GetNames(propertiesEnumType)) { includedProperties.Add(name); } return includedProperties; } 
+2
source

Unfortunately, code analysis tools such as Nitriq or NDepend will not help you, because in the current version they do not record the names and values โ€‹โ€‹of the method arguments.

But you can use Roslyn to create a tool that will analyze your solution and generate a class containing a list with the properties used, to build an event. Code for decision analysis that finds all className calls. methodName and returns its constant arguments (text in your case):

 static IEnumerable<string> GetMethodCallParametersValues(string solutionName, string className, string methodName) { var workspace = Workspace.LoadSolution(solutionName); var solution = workspace.CurrentSolution; var createCommandList = new List<ISymbol>(); var @class = solution.Projects.Select(s => s.GetCompilation() .GetTypeByMetadataName(className)) .FirstOrDefault(); var method = @class.GetMembers(methodName) .AsList() .Where(s => s.Kind == CommonSymbolKind.Method) .FirstOrDefault(); var locations = method.FindReferences(solution) .SelectMany(r => r.Locations); List<string> result = new List<string>(); foreach (var location in locations) { var model = location.Document.GetSemanticModel(); var token = location.Location .SourceTree .GetRoot() .FindToken(location.Location.SourceSpan.Start); var invocation = token.Parent.FirstAncestorOrSelf<InvocationExpressionSyntax>(); var arguments = invocation.ArgumentList.Arguments; result.AddRange(arguments.Select(a => model.GetConstantValue(a.Expression).Value.ToString())); } return result.Distinct(); } 

and code usage:

 var includedProperties = GetMethodCallParametersValues(@"c:\path\to\your.sln", "SomeNamespace.SomeClass", "GetProperty"); 

Note Due to a small Roslyn error while parsing the solution file, you will probably have to comment out the lines below in the .sln file by adding # to make them look:

 # VisualStudioVersion = 12.0.21005.1 # MinimumVisualStudioVersion = 10.0.40219.1 
+2
source

All Articles