I'm writing a source generator in C#, and I've got 2 objects that I need to compare to see if they relate to the same class, but I can't find a way to do it.
My first object is an instance of ClassDeclarationSyntax. This is coming from my custom ISyntaxContextReceiver to find classes that match specific conditions.
Elsewhere in my generator I have an IdentifierNameSyntax object, which is coming from looking at the types within a TypeOfExpressionSyntax that I find within a different class's list of attributes.
I need to compare the two objects here to see if they are talking about the same thing.
With the IdentifierNameSyntax I can get the type information by using the semantic model:
ITypeSymbol semanticType = semanticModel.GetTypeInfo(targetType).Type;
But I don't know how to compare this ITypeSymbol against ClassDeclarationSyntax either.
Is there a way to do this, or is there a way to get the semantic model type information for a ClassDeclarationSyntax object?
The method you are looking for is GetDeclaredSymbol on semanticModel. As you can see from the documentation, there are huge number of overloads which will allow you to get the associated symbol information for not only classes, but also fields, methods, properties, events, parameters, and so forth. Definitely a method you'll want to keep in your back pocket!
Related
I have a variety of methods that use a configuration object to fill in placeholders in a template. Different methods use different subsets of properties of the configuration object. I'd like an easy way to check that all the properties a given method uses are present in a given config object.
Right now I have a method like this:
private static void ValidateConfiguration(CustomerConfiguration config, params string[] properties)
This has the maintenance disadvantage that it relies on a separate set of strings for the properties used. What I'd love to do is have the validation method look at the calling method and see what properties of the config object are being accessed. Can this be done?
(I could also wrap String.Replace() in a method that checks for nulls, but that's less fun.)
A type safe way to handle your problem would be to implement several interfaces with different meaningful subsets of properties. My understanding is that the presence/absence of the properties in your case depends on the type of configuration object and is dynamic.
you could use a signature like that
ValidateConfiguration<T>(CustomerConfiguration config)
where T represent the interface and use reflection to list the required properties. While it would be practically impossible to parse the code of a method to infer its usages of a data structure, reflection on types (to extract properties) is fairly easy.
Different methods use different subsets of properties of the configuration object.
If you're only creating one instance of the configuration property, then the properties it needs to have are whichever ones are going to be used by any method. In other words, if at least one method needs that property, then the object needs that property.
In that case there's no need to validate it in relation to individual methods that need it. All of its properties need to be populated because they're all needed somewhere. If they're not needed anywhere, you can delete them.
Then, instead of validating that object based on the needs of a particular method, you validate it once, perhaps at startup. All of the properties are needed, so if they haven't been specified then the application just can't run. (Sometimes it's good to include defaults in your configuration object. You might have one property that you want to be able to configure, but in real life it's never going to change.)
If you're creating different instances of the same object for use in different methods and you only want to populate certain properties then it's better not to do that. Just create more granular objects for different scenarios containing all the properties you need.
What frequently happens is this: We have an object with lots of properties and we only use a few of them, so we populate those properties and pass the object to a method. The other properties are null.
Then, someone modifying that method decides that they need another property, so they try to use it, and they're surprised to find out that it's null. Then they have to go back and trace where that object was created and figure out what is populated or not. That's confusing and time-consuming.
Unless fields are entirely optional and it doesn't matter whether they are populated or not, we don't want to find ourselves looking at an object with lots of properties and guessing which ones have been populated because individual methods that create the object "know" which properties other classes do or don't need.
I have a large complex XSD set.
I have C# classes generated from those XSDs using xsd.exe. Naturally, though the majority of properties in the generated classes are strings, many are decimals, DateTimes, enums or bools, just as they should be.
Now, I have some UNVALIDATED data that is structured in the correct XML format, but may well NOT be able to pass XSD validation, let alone be put into an instance of the relevant .Net object. For example, at this stage, for all we know the value for the element that should be a DateTime could be "ABC" - not even parseable as a DateTime - let alone other string elements respecting maxLength or regex pattern restrictions. This data is ready to be passed in to a rules engine that we already have to make everything valid, including defaulting things appropriately depending on other data items, etc.
I know how to use the various types in System.Xml to read the string value of a given element by name. Clearly I could just hand craft code to get out all the elements that exist today by name - but if the XSD changes, the code would need to be reworked. I'd like to be able to either directly read the XSD or use reflection on the generated classes (including attributes like [System.Xml.Serialization.XmlTypeAttribute(TypeName=...] where necessary) to find exactly how to recursively query the XML down to the the raw, unverified string version of any given element to pass through to the ruleset, and then after the rules have made something valid of it, either put it back into the strongly typed object or back into a copy of the XML for serialization into the object.
(It has occurred to me that an alternative approach would be to somehow automatically generate a 'stringly typed' version of the object - where there are not DateTimes etc; nothing but strings - and serialize the xml into that. I have even madly thought of taking the xsd.exe generated .cs file and search/replacing all the enums and base types that aren't strings to strings, but there has to be a better way.)
In other words, is there an existing generic way to pull the XElement or attribute value from some XML that would correspond to a given item in a .Net class if it were serialized, without actually serializing it?
Sorry to self-answer, and sorry for the lack of actual code in my answer, but I don't yet have the permission of my employer to share the actual code on this. Working on it, I'll update here when there is movement.
I was able to implement something I called a Tolerant XML Reader. Unlike most XML deserializing, it starts by using reflection to look at the structure of the required .Net type, and then attempts to find the relevant XElements and interpret them. Any extra elements are ignored (because they are never looked for), any elements not found are defaulted, and any elements found are further interpreted.
The main method signature, in C#, is as follows:
public static T TolerantDeserializeIntoType<T>(
XDocument doc,
out List<string> messagesList,
out bool isFromSuppliedData,
XmlSchemaSet schemas = null,
bool tolerant = true)
A typical call to it might look like this:
List<string> messagesList;
bool defaultOnly;
SomeType result = TolerantDeserializeIntoType<SomeType>(someXDocument, out messagesList, out defaultOnly);
(you may use var; I just explicitly put the type there for clarity in this example).
This will take any XDocument (so the only criteria of the original was that it was well-formed), and make an instance of the specified type (SomeType, in this example) from it.
Note that even if nothing at all in the XML is recognized, it will still not fail. The new instance will simply have all properties / public fields nulled or defaulted, the MessageList would list all the defaulting done, and the boolean out paramater would be FALSE.
The recursive method that does all the work has a similar signature, except it takes an XElement instead of an XDocument, and it does not take a schemaSet. (The present implementation also has an explicit bool to indicate a recursive call defaulting to false. This is a slightly dirty way to allow it to gather all failure messages up to the end before throwing an error if tolerant is false; in a future version I will refactor that to only expose publicly a version without that, if I even want to make the XElement version public at all):
public static T TolerantDeserializeXElementIntoType<T>(
ref XElement element,
ref List<string> messagesList,
out bool isFromSuppliedValue,
bool tolerant = true,
bool recursiveCall = false)
How it works, detail
Starting with the main call, the one with with an XDocument and optional SchemaSet:
If a schema Set that will compile is supplied (actually, it also looks for xsi:noNamespaceSchemaLocation as well) the initial XDocument and schemaSet call runs a standard XDocument.Validate() across the supplied XDocument, but this only collects any issued validation error callbacks. It won't throw an exception, and is done for only two reasons:
it will give some useful messages for the MessageList, and
it will populate the SchemaInfo of all XElements to
possibly use later in the XElement version.
(note, however, that the
schema is entirely optional. It is actually only used to resolve
some ambiguous situations where it can be unclear from the C#
object if a given XElement is mandatory or not.)
From there, the recursive XElement version is called on the root node and the supplied C# type.
I've made the code look for the style of C# objects generated by xsd.exe, though most basic structured objects using Properties and Fields would probably work even without the CustomAttributes that xsd.exe supplies, if the Xml elements are named the same as the properties and fields of the object.
The method looks for:
Arrays
Simple value types, explicitly:
String
Enum
Bool
then anything
else by using the relevant TryParse() method, found by reflection.
(note that nulls/xsi:nill='true' values also have to be specially
handled)
objects, recursively.
It also looks for a boolean 'xxxSpecified' in the object for each field or property 'xxx' that it finds, and sets it appropriately. This is how xsd.exe indicates an element being omitted from the XML in cases where null won't suffice.
That's the outline. As I said, I may be able to put actual code somewhere like GitHub in due course. Hope this is helpful to someone.
I have a predefined data format, which requires me to have an object like this:
settings:{
settingA:'someValueFromSql',
settingB:'someValueFromAD'
settingC:'someValueFromConfigFile',
settingD:'someValueFromReflection',
settingE:42,
...
}
This settings object is in fact a huge mess of data stitched together from many different sources, but this is how the data is expected by the frontend. I'd like to put the "get/process all data from one source" in a function each, and tape the object together in the end.
So I would have one object
sqlSettings:{
settingA:'someValueFromSql',
settingG:'someOtherValueFromSql',
...
}
returned by function a, and an object
adSettings:{
settingB:'someValueFromAD',
settingV:'someOtherValueFromAD',
...
}
returned by function b, and an object
settings includes adSettings, sqlSettings
where, with at most two simple steps, I can "join together" both objects into a flat third object.
Is this possible with fixed-size objects, without using a generic Dictionary, or am I barking up the wrong tree?
(I'm sure this question was already asked on SO, but I guess I don't find the right words)
It's not possible with a "normal" object, but you can do it with an ExpandoObject and the dynamic keyword. But you need at least .net 4.0
dynamic settings = new ExpandoObject();
//If you try to assign a property that doesn't exist, it is added to the object.
settings.SettingA="sfgd"
Anyway I discourage using this approach. why you don't want to use a IDictionary<string, object> or better a IDictionary<string, MyCustomSettingObject>
--------------------UPDATE---------------------
if the only thing that stops you from using a dictionary is the serialization you can implement the IXmlSerializable Interface and ouput the xml you like:
Proper way to implement IXmlSerializable?
If those partial setting objects have fixed size (meaning fixed number of properties), then you can definitely create a flat object with e.g. only properties to fit all the values in. Then to ease your work, you can try to use Automapper to map the partial objects to the "grouped" object.
http://automapper.codeplex.com/
Otherwise, you will have to stick with the dictionary.
I have two objects (WS.Customer and EF.Customer). The reason for this is my vendor didn't expose all of the fields in their object (WS.Customer) and I need to insert and update into those fields.
I need to merge WS.Customer -> EF.Customer and later merge EF.Customer -> WS.Customer. EF.Customer will have some extra fields that WS.Customer won't have, but when the field names match - I want the values merged.
I also only want to merge values where the destination field is null, empty, or a default value in case of a Guid.
I know I could use to Linq to query each object and build the other, but is there a less verbose way of doing things? I have some other objects I need to use this approach for and don't feel like spending a weeks typing away.
Thanks
You can use one of the available object-to-object mappers library like AutoMapper or EmitMapper. They will take care of copying the data in both directions and skip fields if properly configured. For example with EmitMapper your code might look like this:
ObjectMapperManager.DefaultInstance
.GetMapper<WS.Customer, EF.Customer>(<your configuration object here>)
.Map(customerSource, customerDestination);
What do you mean by "merged"? I guess you need to "translate" from one instance to another, i.e. copy values when name and type of property matches. Please have a look at the implementation provided in ServiceStack, the extension method of object - TranslateTo method: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Common/ReflectionExtensions.cs#L31
I have three different classes:
Task
Order
Transmission
Each class have properties with different types. Also, there is a possibility to attach data that represented by custom fields (implemented by an array of IField, where IField can be text field or list field). Each custom field have a name that represent the name of the attached data property.
I need to convert between each class to another:
Task -> Order
Order -> Transmission
Transmission -> Task
Transmission -> Order
Order -> Task
Task -> Transmission
for that I created:
Static class of static keys where each key represents the name of
the property.
"DataObject" that holds a dictionary of a property name and an object as its value.
Each class (Task, Order, Transmission) implements IDataShare interface:
public interface IDataShare
{
DataObject ToDataObject();
void FromDataObject(DataObject data);
}
For example, task objects with the following properties:
WorkerId:5
CustomerId:7
VehicleId:null
StartDate:null
And with the following custom fields:
Subcontractor: {listId:5, Value:4} (this is list field)
delivery Note: "abc" (this is text field)
will be convert to the following dictionary:
{"WorkerId", 5}
{"CustomerId", 7}
{"VehicleId", null}
{"StartDate", null}
{"Subcontractor", {listId:5, Value:4}}
{"delivery Note", "abc"}
the string keys "WorkerId", "CustomerId", "VehicleId", "StartDate" were taken from static class that contains string consts where "Subcontractor" and "deliveryNote" are the names of the custom fields the user added (I don't know which fields the user might add so I just use the field name).
When I fill an object using DataObject I have to verify the name of the property is the same as the name of the key and also verify the value is correct (string cannot inserted into long).
In addition, custom list field (subcontractor) can't have only itemId as a value because when I have to verify that the listId of the custom field in the object is the same with the listId of the customField in the DataObject.
I have many problems about knowing the type of the value. I always have to use "X is Y" if statements of "X as Y" statements. In addition, I have to remember how to store the type of the value when implementing IDataShare interface which makes the work harder.
Can anyone help me think about constraint I can add to the conversion proccess from an object to DataObject? Can anyone help me improve this method of converting objects?
Thanks!
UPDATE
I want to explain a point. My biggest problem is that there are several ways to translate each property/custom field so I need to remember the type of the value in DataObject. For example, in Transmission class I have VehicleId property. I want to convert Task object with custom field with the name "VehicleId" to Transmission. All I want is that Task's custom field VehicleId's value will be converted into the VehicleId property of Transmission. But, because it is custom field - as I wrote before - there is a way I store custom field that based on a list: {listId:5, Value:4}. Now, in the conversion proccess (FromDataObject in Transmission) in case the DataObject has "VehicleId" key, I have to check whether the value is long (vehicle id as property) or IListField (vehicle id as custom list field).
those type checking really makes mess.
Well, if the number of classes you're converting between is really as limited as you've said, may I suggest just writing casting operators for your classes?
http://msdn.microsoft.com/en-us/library/xhbhezf4%28v=VS.100%29.aspx
It seems like the amount of logic that you're putting into the conversion is enough to warrant something like this.
On the other hand, it seems like there is a base set of fields being used across the different objects and you're just stuffing them into an untyped dictionary. If the fields are common across all types, could you use a conversion to a strongly typed common object?
Which also begs the question: could you use a common base class object?
If you have options of modifying the Task, Order, and Transmission definitions, I'd take a look at them again. This sort of scenario seems like a "code smell".
If I understand this correctly ToDataObjectis basically a serializer and FromDataObject is a deserializer. If the data contained by these object is type compatible, then it seems that the very act of serializing it into untyped data is the source of your problem. Why do this, instead of just keeping the data in its native format?
If you need to use an adapter because there are incompatibilities between the objects that can't be resolved for some reason, I would think that you can make one that at least keep the data in its native structures instead of serializing everything to a string. A dictionary in C# can contain anything, at a minimum you could be using a Dictionary<string,object>.
It's also unclear what all this verification is about - why would data be incompatible, if you are mapping properties of the same data types? E.g. assuming that this is an internal process, under what circumstance could (e.g.) a string from one object be trying to be assigned to a long in another object? Seems that would only be necessary if the data were strongly typed in one object, but not in another.
Have you considered using generics?
If Task, Order and Transmission all inherit from a base class like Property, then you could expose a common method for getting the values you need.
GetMyValue() where T : Property
It's not very clear what you are trying to achieve.