Dynamically find used properties prior to them being used - c#

I am looking for optimization of a pattern I am using for a dynamic forms application.
I have a repository class with a method:
public Entity Find(string objectId, List<string> includedProperties);
This returns an Entity object, with only the fields specified in the "includedProperties", as building the entire object for all purposes is unnecessary overhead in this case (some entities have hundreds of properties).
Example domain code using this repository often looks something like this:
var includedProperties = new List<string> {
"FirstChildName" ,
"FirstChildDob",
"SecondChildName",
"SecondChildDob"
}
I then fetch an object:
var person = repository.Find("123",includedProperties);
I then use the Properties with a GetProperty(string propertyName) method:
var firstChildDob = person.GetProperty("FirstChildDob").AsDateTime();
...etc
This all works fine, and fits well with the dynamic design of the application. However, I find it irritating that I always need to declare a list of "used" properties separately, prior to fetching the object.
So, my question is, through reflection or some other cleverness, can I simplify the building of the "Included Properties" by looking at which parameters get passed later in the code with the "GetProperty" method?
Using the above example, I'd like to build the list using a helper like this (or similar):
var includedProperties = HelperObject.GetFieldsUsedInCurrentCodeFile();
This would somehow pickup what string constants were passed to the "GetProperty()" method, saving the need for explicit declaration. Any suggestions welcome!

I actually had a similar problem awhile back; the best I could come up with at the time was to define an enum that contained the names of the properties I wanted to use in the method.
Using this approach, you could build the list of included properties by cycling through the enum.
There are a couple of benefits to this approach vs. strings:
Any property spelling issues or property name changes are made in a single location.
If you are using a tool such as Resharper, you can determine when you have unused "properties" in the enum.
For example:
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;
}

Code analysis tools like Nitriq or NDepend unfortunately won't help you because in current version they do not capture method's arguments names and values.
But you can use Roslyn to create tool that will analyze your solution and generate a class containing list with used properties as a pre-build event. Code for solution analysis that finds all calls to className.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: Because of Roslyn's small bug in parsing Solution file, you probably will have to comment below lines in .sln file by adding # so they will look:
# VisualStudioVersion = 12.0.21005.1
# MinimumVisualStudioVersion = 10.0.40219.1

Related

Elegant ITuple equivalent (named members + iteration)

I am trying to clean up my code by declaring some string literals in one place instead of using them directly. For my use case, I need to access each separately, and later do something with all the values. One solution would be making a new class for each such scenario and backing the properties with a Dictionary<string, string>, but I'd like to avoid bloating the codebase.
I have a working solution using anonymous objects, whose properties are then read using reflection. But ideally, I'd like to get by without it.
var fields = new { DescriptiveName1 = "PREFIX_COL_XYZ", DescriptiveName2 = "PREFIX_THING_ABC" };
var relevantValue = originalData.GetSomeValueAs<int>(fields.DescriptiveName1);
// etc.
var keyValuesForReporting = GatherReportingData(originalData, fields);
IReadOnlyDictionary<string, string> GatherReportingData<T>(OriginalData data, T fields)
=> typeof(T) // ...
There is the ITuple interface, which would make this easy. Unfortunately, I don't have access to it on this project (required to use .NET Standard 2.0 for interoperation).
The code before my refactoring efforts looked something like this:
var relevantValue = originalData.GetSomeValueAs<int>("PREFIX_COL_XYZ");
var relevantValue = originalData.GetSomeValueAs<int>("PREFIX_THING_ABC");
// ...
var fieldsUsedInAlgorithm= new[] { "PREFIX_COL_XYZ", "PREFIX_THING_ABC" }; // notice the duplicated string literal
var keyValuesForReporting = GatherReportingData(originalData, fieldsUsedInAlgorithm);

Dynamically get a DbSet<T> by Entity class name

I'm trying to use System.Reflections to get a DbSet<T> dynamically from its name.
What I've got right now is:
The DbSet name
The DbSet's Type stored on a variable
The issue I'm facing comes out when trying to use the dbcontext.Set<T>() method, since (these are my tries so far):
When I try to assign to <T> my DbSet Type, it throws me the following compilation error:
"XXX is a variable but is used like a type"
If I try with using both the Extension methods that you will find below in my code (which I made in order to try to get an IQueryable<T>), it returns a IQueryable<object>, which unfortunately is not what I am looking for, since of course when I try to manipulate it with further Reflections, it lacks of all the properties that the original class has…
What am I doing wrong? How can I get a DbSet<T>?
My code is the following, but of course, let me know if you need more infos, clarifications or code snippets.
My Controller's Method:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//FIRST TRY
//throws error: "_type is a variable but is used like a type"
var tableSet = _context.Set<_type>();
//SECOND TRY
//returns me an IQueryable<object>, I need an IQueryable<MyType>
var tableSet2 = _context.Set(_type);
//THIRD TRY
//always returns me am IQueryable<object>, I need an IQueryable<MyType>
var calcInstance = Activator.CreateInstance(_type);
var _tableSet3 = _context.Set2(calcInstance);
//...
}
Class ContextSetExtension
public static class ContextSetExtension
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
var res= _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
return (IQueryable<object>)res;
}
public static IQueryable<T>Set2<T>(this DbContext _context, T t)
{
var typo = t.GetType();
return (IQueryable<T>)_context.GetType().GetMethod("Set").MakeGenericMethod(typo).Invoke(_context, null);
}
}
EDIT Added TypeFinder's inner code.
In brief, this method does the same of Type.GetType, but searches Type on ALL the generated assemblies
public class TypeFinder
{
public TypeFinder()
{
}
public static Type FindType(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
var result = (from elem in (from app in assemblies
select (from tip in app.GetTypes()
where tip.Name == name.Trim()
select tip).FirstOrDefault())
where elem != null
select elem).FirstOrDefault();
return result;
}
}
UPDATE as requested in the comments, here's the specific case:
In my DB i've got some tables which are really similar each other, so the idea was to create a dynamic table-update method which would be good for every table, just passing to this method the table name, the ID of the row to update and the JSON containing data to update.
So, in brief, I would perform some updates on the table given in input as DbSet type, updating the row with ID==id in input with the data contained inside the JSON, which will be parsed inside an object of type X(the same of dbset)/into a dictionary.
In pseudo-code:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//THIS DOESN'T WORKS, of course, since as said above:
//<<throws error: "_type is a variable but is used like a type">>
var tableSet = _context.Set<_type>();
//parsing the JSON
var newObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonupdate, _type);
//THIS OF COURSE DOESN'T WORKS TOO
//selecting the row to update:
var toUpdate = tableSet.Where(x => x.Id == id).FirstOrDefault();
if(toUpdate!=null)
{
var newProperties = newObj.GetType().GetProperties();
var toUpdateProperties = toUpdate.GetType().GetProperties();
foreach(var item in properties)
{
var temp = toUpdateProperties.Where(p => p.Name==item.Name)
{
//I write it really in briefand fast, without lots of checks.
//I think this is enough, I hope
temp.SetValue(toUpdate, item.GetValue());
}
}
_context.SaveChanges();
}
return false;
}
returns me an IQueryable<object>, I need an IQueryable<MyType>
Well, that will never work. Your IQueryable cannot be of type IQueryable<MyType>because that would mean the compiler would need to know what MyType is and that is not possible, because the whole point of this exercise is to decide that on runtime.
Maybe it's enough to know that those objects are in fact instances of MyType?
If not, I think you have painted yourself into a corner here and you are trying to figure out what paint to use to get out of there. Take a step back, it's probably not a technical problem. Why do you need to do this? Why do you have the conflicting needs of knowing the type at runtime only and knowing it at compile time?
You need to think about your requirements, not about the technical details.
I needed to dynamically load a single record from the database for each type in a list of known types, to print a test email when an admin is editing the template, so I did this:
List<object> args = new List<object>();
//...
//other stuff happens that isn't relevant to the OP, including adding a couple fixed items to args
//...
foreach (Type type in EmailSender.GetParameterTypes())
{
//skip anything already in the list
if (args.Any(a => a.GetType().IsAssignableFrom(type))) continue;
//dynamically get an item from the database for this type, safely assume that 1st column is the PK
string sql = dbContext.Set(type).Sql.Replace("SELECT", "SELECT TOP 1") + " ORDER BY 1 DESC";
var biff = dbContext.Set(type).SqlQuery(sql).AsNoTracking().ToListAsync().Result.First();
args.Add(biff);
}
Caveat: I know at least one record will exist for all entities I'm doing this for, and only one instance of each type may be passed to the email generator (which has a number of Debug.Asserts to test validity of implementation).
If you know the record ID you're looking for, rather than the entire table, you can use dbContext.Set(type).Find(). If you want the entire table of whatever type you've sussed out, you can just do this:
string sql = dbContext.Set(type).Sql; //append a WHERE clause here if needed/feasible, use reflection?
var biff = dbContext.Set(type).SqlQuery(sql).ToListAsync().Result;
Feels a little clunky, but it works. There is strangely no ToList without Async, but I can run synchronously here. In my case, it was essential to turn off Proxy Creation, but you look like you want to maintain a contextful state so you can write back to db. I'm doing a bunch of reflection later, so I don't really care about strong typing such a resulting collection (hence a List<object>). But once you have the collection (even just as object), you should be able to use System.Reflection as you are doing in your UPDATE sample code, since you know the type and can use SetValue with known/given property names in such a manner.
And I'm using .NET Framework, but hopefully this may translate over to .NET Core.
EDIT: tested and working:
public async Task<bool> MyMethod(string _type)
{
Type type = Type.GetType(_type);
var tableSet = _context.Set(type);
var list = await db.ToListAsync();
// do something
}
// pass the full namespace of class
var result = await MyMethod("Namespace.Models.MyClass")
IMPORTANT NOTE: your DbContext need to have the DbSet declared to work!
public class MyContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
}

Roslyn CTP - Random Code Modification

I have been experimenting with the Roslyn API in a genetic programming type of scenario. It appears like a great way to do that type of programming, but the actual implementation of simple scenarios doesn't seem very straightforward, which means I probably don't have a good understanding of how to use this API properly. Here is a simple program I am trying to modify in my experiments:
string treeText = #"using System;
using System.Collections.Generic;
namespace TestProgram
{
class Program
{
static void Main(string[] args)
{
var myVar = 3;
string myString = ""Hello World"";
List<string> stringList = new List<string>();
Console.WriteLine(myString + myVar);
Console.ReadLine();
}
}
}";
SyntaxTree tree = SyntaxTree.ParseText(treeText);
var compilation = Compilation.Create("test.exe",
syntaxTrees: new[] { tree },
references: new[]
{
new MetadataFileReference(typeof(object).Assembly.Location),
new MetadataFileReference(typeof(Enumerable).Assembly.Location),
});
SemanticModel model = compilation.GetSemanticModel(tree);
Just as a simple example let's say I somehow "randomly" decided I want to insert a new method invocation using the myString instance. What would be an efficient way to figure out what methods I could invoke off of that instance? What would then be the best way to create the necessary MethodInvocationSyntax (once I chose a specific method to use) from the symbol information? I found a method called ResolveOverloads on the SemanticModel class which appears where I need to end up, but I am having some difficulty figuring out an efficient path to the parameters this method requires. Is that even the right path to go down?
First, get the VariableDeclaratorSyntax for your variable, for example:
var variable = tree.GetRoot().DescendantNodes()
.OfType<VariableDeclaratorSyntax>()
.Single(v => v.Identifier.ValueText == "myString");
Next, get the LocalSymbol for this variable from the SemanticModel:
var variableSymbol = (LocalSymbol)model.GetDeclaredSymbol(variable);
Then you can get a list of methods you can invoke on this variable based on its type. You can either simply get all members of the type that are instance methods:
var methods =
variableSymbol.Type.GetMembers()
.OfType<MethodSymbol>()
.Where(m => !m.IsStatic && m.MethodKind == MethodKind.Ordinary);
Or, if you wanted to include extension methods, you could use LookupSymbols():
var methods = model.LookupSymbols(
variable.GetLocation().SourceSpan.Start, variableSymbol.Type,
options: LookupOptions.IncludeExtensionMethods)
.Where(m => !m.IsStatic);
You can then choose one of the available methods based on your logic and create InvocationExpressionSyntax (the following code assumes it's a parameterless method):
var invocationExpression =
Syntax.InvocationExpression(
Syntax.MemberAccessExpression(
SyntaxKind.MemberAccessExpression,
Syntax.IdentifierName(variableSymbol.Name),
(SimpleNameSyntax)Syntax.ParseName(selectedMethod.Name)));
You would then need to figure out where in your method to add the expression and what to do with its result (if anything).

Can roslyn generate enums?

So, I feel like it's possible, but i don't have the chops to put it together. I have lookup tables (with ID and Name fields). I have enums that I keep in sync with the lookups. What I'd like is to make a list of SQL tables, and have roslyn create the enums during/before compile. this way I know my enums are always in sync, and it's one more thing off my plate.
Could someone out there show me the way, or tell me why it would be a bad idea? I feel like the solution isn't really a lot of code...
Thanks!
This is how you create enums with roslyn;
class Program
{
static void Main(string[] args)
{
var member1 = Syntax.EnumMemberDeclaration(
identifier: Syntax.Identifier("Member1")
);
var declaration = Syntax.EnumDeclaration(
identifier: Syntax.Identifier("MyEnum"),
modifiers: Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword)),
members: Syntax.SeparatedList(member1)
);
Console.WriteLine(declaration.Format());
Console.ReadLine();
}
}
this returns:
public enum MyEnum
{
Member1
}
Now to your question, I don't know if this is what you really need.
First I think there is not yet any built in functionality to do this at precompile, so you'd need to create a console exe and call it in your prebuild events.
So at this point probably it is a better idea to do this manually via code generation and not on every build, unless these tables are changing very frequently.
An in this case you don't really need roslyn to do it for you, you can just spit it out with any code generation software, or roll your own...
Here is some code updated for the current version of Roslyn and also shows adding attributes and values to the enum items.
public EnumDeclarationSyntax GenerateEnum()
{
var loanPurpose = new[]
{
"Business Launching",
"HomePurchase",
"HomeImprovement"
};
var enumDescriptions = new[]
{
"Launch a new business",
"Buy a home",
"Make home improvements"
};
var i = 0;
var members = new List<EnumMemberDeclarationSyntax>();
foreach (var item in loanPurpose)
{
var attribute = SyntaxFactory.Attribute(
SyntaxFactory.IdentifierName("Description"));
var attributeArgument = SyntaxFactory.AttributeArgument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(enumDescriptions[i ])));
attribute = attribute.WithArgumentList(
SyntaxFactory.AttributeArgumentList(
SyntaxFactory.SingletonSeparatedList(attributeArgument)));
var attributes = SyntaxFactory.SingletonList(
SyntaxFactory.AttributeList(SyntaxFactory
.SingletonSeparatedList(attribute)));
var objectCreationExpression = SyntaxFactory.EqualsValueClause(
SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression,
SyntaxFactory.Literal(i)));
var member = SyntaxFactory.EnumMemberDeclaration(attributes,
SyntaxFactory.Identifier(item),
objectCreationExpression);
members.Add(member);
i++;
}
var declaration = SyntaxFactory.EnumDeclaration
(new SyntaxList<AttributeListSyntax>(),
baseList: null,
identifier: SyntaxFactory.Identifier("LoanPurpose"),
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)),
members: SyntaxFactory.SeparatedList(members)
);
return declaration;
}
The syntax for adding the attributes gets a little awkward but I find that I often need some attributes added. You probably already have the data for the attributes and values in the lookup tables driving the code generation.
The Answer provided by Sebastian 6 years ago likely used to work but no longer does.
New set of sample code to accomplish the same thing as of 18/12/2017
EnumMemberDeclarationSyntax member1 = EnumMemberDeclaration(identifier: Identifier("Member1"));
var members =new SyntaxNodeOrToken[]{member1};
var declaration = EnumDeclaration(
identifier: Identifier("MyEnum")).WithMembers(
members: SeparatedList< EnumMemberDeclarationSyntax>(members));

Calling custom (formatting) method on LINQ to Entities

I'm using EF 4.1 and I'm trying to enumerate a company list for a grid. I have two options in the current project: select all companies from the DbContext (Entities) and load them into an object from a non-anonymous type (let's say EmpresaGrid) or select all companies into anonymous type objects with the same structure like Empresa (which is the entity I'm selecting from).
The first option (creating a model class for that) would require a little more work, but can be, eventually, more readable. Still, I'm not sure about that. The second option is what I'm using right now.
So, first question: it's better to create a model class only for displaying data or use anonymous type? Doing a direct select is out of question: a SELECT * is too big and that might make everything damn slow (I guess). So selection into another type creates a custom query with only the needed fields.
Using the second option (anonymous type), I have this code (simplified version):
public static IEnumerable<object> Grid()
{
Entities db = new Entities();
var empresas = db.Empresas
.Select(e => new
{
Cgc = e.Cgc, // PK
(...)
Address = new
{
AddressLine = e.EnderecoSede.AddressLine,
(...)
}
},
Contato = e.Contato,
(...)
})
.ToList();
return empresas;
}
The anonymous type I'm creating has around 40 lines of code, so it's kinda big, but it recreates part of the Empresa class struct (since the grid is waiting for a Empresa object). Anyway, I have a problem with the data format. For example, I would like to format the Cgc property using a custom string format. I have a public method for this, FormataCgc. This method receives a string and returns it formatted using some internal conditions.
So, my problem is how to that. For example, I have tried this:
var empresas = db.Empresas
.Select(e => new
{
Cgc = FormataCgc(e.Cgc),
}
But that doesn't work because FormataCgc cannot be translated into SQL (and I don't want to convert it). I also tried this:
var empresas = db.Empresas
.Select(e => new
{
(...)
}
.ToList();
foreach (var e in empresas) {
e.Cgc = FormataCgc(e.Cgc);
}
But it cannot be done since anonymous types have only read-only properties.
So, my second question is: how exactly can I do that? I need to change the data after selecting it, but using anonymous types? I've done a little research, and the best thing I've found was this: Calling a custom method in LINQ query. In that solution, Ladislav suggested doing a second select from the IEnumerable, but since the grid is excepting Empresa I cannot do that (I need to change or add properties, not encapsulate them).
I'm not sure if I was clear enough, but feel free to ask any questions. Also, the grid I'm currently using is a Telerik ASP.NET MVC Grid, which receives a IEnumerable (where T is a class) as model data and them iterates each object, doing its magic.
Since you're already converting this into an IEnumerable<T>, you can do the custom formatting as you stream the results in the client. Do your db.Select, and then convert to the appropriate format afterwards, ie:
var empresas = db.Empresas
.Select(e => new
{
(...)
})
.ToList();
foreach (var e in empresas) {
yield return new {
Cgc = FormataCgc(e.Cgc),
// Copy other properties here, as needed...
};
}
That being said, I'd personally recommend making a custom class, and not return an anonymous type. Your conversion would then be:
foreach (var e in empresas) {
yield return new YourClass(FormataCgc(e.Cgc), ...); // Construct as needed
}
This will dramatically improve the usability of this method, as you will have proper, named access to your properties from the caller of the method.
I think the solution to both of your questions is to create a model class. Sure it is a little bit more work up front, but it will allow you greater flexibility in the long run. Your custom model class can then handle the formatting for you.
public class EmpresaGridModel
{
public string Cgc { get; set; }
public string CgcFormatted
{
return FormataCgc(this.Cgc);
}
//properties for the other fields will have to be created as well obviously
}
Your telerik grid can then bind directly to the CgcFormatted property

Categories