How to turn class properties into parameters?

Background

I work with QueryExpressions Microsoft Dynamics CRM 2011, which for all of you who are not CRM people, I just know that I work with the SDK to access a database in which you need to specify the string names of the columns you select in the ColumnSet custom class:

new ColumnSet("personid", "name", "age"); 

The SDK creates early classes, so I have classes for all database tables, and early related classes have a dictionary whose key is the columns of the table that were populated by the object. i.e:

 var p = new Person { Name = "John", Age = 39, SSN = null }; p.Attributes.Count == 3; // p.Attributes.Keys == { "name", "age", "ssn" }; 

Problem

I have three problems populating a ColumnSet

  • I forgot which columns the class / table contains.
  • I pushed some of the columns and get no error until runtime
  • Column naming should be lowercase when instantiating a ColumnSet, which makes it poorly readable, i.e. thissillyexampleisntthatreadable vs thissillyexampleisntthatreadable

All three of these problems can be resolved by early binding.

I know that I can create an enum or stuct for each class that contains all the columns of the classes, that is: the new ColumnSet (PersonColumns.PersonId, PersonColumns.Name, PersonColumns.Age), but I want it to use the classes that are already created .

My best attempt

The best I can think of is the following:

 ColumnSetFactory.Create<Person>(p => p.PersonId = null, p.Name = null, p.Age = null); 

Here, Create takes an object of type T (the person in this example), and then checks the dictionary of the object to generate and returns a ColumnSet.

goal

You have a generic function that uses early related classes to create a ColumnSet:

 ColumnSetFactory.Create<Person>(p => p.PersonId, p.Name, p.Age); 

Any ideas?

+4
source share
2 answers

The syntax of your attempt / target does not work in C #, unfortunately, but you can probably get something close to it.

I am not familiar with the dynamics of crm, so I make this assumption, the constructor for ColumnSet takes a variable number of string arguments and has a signature:

 public ColumnSet(params string[] arguments) 

You can create a lambda expression that returns the initializer of the object (to create an anonymous object) and use some reflection to invoke the constructor using the bindings of the initializer elements. If I understand what you're trying to accomplish, you want to use the existing object parameter names to pass these names to this constructor to create the object.

Here's how you can do it:

 public static ColumnSet Create<T>(Expression<Func<T, object>> parameters) { var initializer = parameters.Body as NewExpression; if (initializer == null || initializer.Members == null) throw new ArgumentException("lambda must return an object initializer"); var memberNames = initializer.Members .Select(member => member.Name.ToLower()) .ToArray(); var ctor = typeof(ColumnSet).GetConstructor(new Type[] { typeof(string[]) }); return (ColumnSet)ctor.Invoke(new object[] { memberNames }); } 

Then, to use it, name it as follows:

 ColumnSetFactory.Create<Person>(p => new { p.PersonId, p.Name, p.Age }); // Personally I prefer to call it like this to let the compiler // infer the generic arguments ColumnSetFactory.Create((Person p) => new { p.PersonId, p.Name, p.Age }); 

To generate an equivalent call to the constructor:

 new ColumnSet("personid", "name", "age"); 

You can even make column names and give them random values, the values ​​themselves will not be used, just the name of the member.

 ColumnSetFactory.Create<Person>(p => new { p.PersonId, p.Name, p.Age, Foobar = 0, Boo = "rawr!!!", }); 

To generate an equivalent call to the constructor:

 new ColumnSet("personid", "name", "age", "foobar", "boo"); 
+3
source

There are several moving parts.

First, with C #, you need early related elements with late execution, and unfortunately, you cannot get there as you want. Early binding creates a class before compilation time.

Thus, one option would be to create your own simplified early creation for several objects that you want (or use early Microsoft-related materials)

Another option is to use a dictionary - but it will probably be more tedious and only take care of issue number 3.

I know this is not the answer you are looking for, but I would take a step back and look at using Fetch.

With a tool like stunnware, you can easily create your sample query and test it to get the results you are looking for.

Then just create a generic function to do fetch to return IList>. Thus, you can easily refer to fields based on the attributes that you specified in the selection. (Caring for issues No. 1 and No. 2)

0
source

All Articles