Converting a MEF application to utilize Rx (System.Reactive) - c#

So the current situation is I have a program that is completely utilizing MEF. Now I want to make it utilize Rx as to allow it to scale to larger queries and allow the user to look over results as the various plugins return results. It is currently setup as such:
Workflow: Query => DetermineTypes => QueryPlugins => Results
Currently the code is all stored on GitHub if anyone needs to reference more than what I post below.
ALeRT on GitHub
The VS solution has a UI project (default StartUp Project), a PluginFramework Project, various TypePlugin Projects (think determining what the type is such as a URL, Email, File, Phone Number, etc) and also QueryPlugin Projects (perform xyz if the queryplugin supports the type that has been determined). All the results are displayed back into the UI by ways of a DataGrid that is being mapped to by the DefaultView of a DataTable.
I want to try and make the Rx portion as invisible to the plugins as possible. This is due to the fact that I do not want to make writing plugins complex for the few people that will. So I was thinking about taking the current Framework below:
public interface IQueryPlugin
{
string PluginCategory { get; }
string Name { get; }
string Version { get; }
string Author { get; }
System.Collections.Generic.List<string> TypesAccepted { get; }
string Result(string input, string type, bool sensitive);
}
and making the Result method into the following:
System.IObservable<string> Result(string input, string type, bool sensitive);
This would naturally require modifying the method that is calling the plugin which as it stands is:
using (GenericParserAdapter parser = new GenericParserAdapter())
{
using (TextReader sr = new StringReader(qPlugins.Result(query, qType, sensitive)))
{
Random rNum = new Random();
parser.SetDataSource(sr);
parser.ColumnDelimiter = Convert.ToChar(",");
parser.FirstRowHasHeader = true;
parser.MaxBufferSize = 4096;
parser.MaxRows = 500;
parser.TextQualifier = '\"';
DataTable tempTable = parser.GetDataTable();
tempTable.TableName = qPlugins.Name.ToString();
if (!tempTable.Columns.Contains("Query"))
{
DataColumn tColumn = new DataColumn("Query");
tempTable.Columns.Add(tColumn);
tColumn.SetOrdinal(0);
}
foreach (DataRow dr in tempTable.Rows)
{
dr["Query"] = query;
}
if (!resultDS.Tables.Contains(qPlugins.Name.ToString()))
{
resultDS.Tables.Add(tempTable);
}
else
{
resultDS.Tables[qPlugins.Name.ToString()].Merge(tempTable);
}
pluginsLB.DataContext = resultDS.Tables.Cast<DataTable>().Select(t => t.TableName).ToList();
}
}
So at this point I'm stuck as to how to make this work. There doesn't seem to be good documentation on how to integrate MEF with Rx. My assumption is to make the following change
using (TextReader sr = new StringReader(qPlugins.Result(query, qType, sensitive).Subscribe()))
but this isn't going to work. So any help on making these changes would be greatly appreciated. If you have other suggestions regarding my code, please let me know. I do this as a hobby so I know my code surely isn't up to snuff for most people.

Would this work for you:
IObservable<DataTable> q =
from text in qPlugins.Result(query, qType, sensitive)
from tempTable in Observable.Using(
() => new GenericParserAdapter(),
parser => Observable.Using(
() => new StringReader(text),
sr => Observable .Start<DataTable>(
() =>
{
var rNum = new Random();
parser.SetDataSource(sr);
parser.ColumnDelimiter = Convert.ToChar(",");
parser.FirstRowHasHeader = true;
parser.MaxBufferSize = 4096;
parser.MaxRows = 500;
parser.TextQualifier = '\"';
var tempTable = parser.GetDataTable();
tempTable.TableName = qPlugins.Name.ToString();
if (!tempTable.Columns.Contains("Query"))
{
DataColumn tColumn = new DataColumn("Query");
tempTable.Columns.Add(tColumn);
tColumn.SetOrdinal(0);
}
foreach (DataRow dr in tempTable.Rows)
dr["Query"] = query;
return tempTable;
})))
select tempTable;

Related

gRPC StarLink Router .Net

New Info:
I thought I would paste this in full as I can not seem to find any samples on the web of a c# solution for StarLink so hopefully anyone else looking for something may find this helpful and may contribute.
My New Proto File - (partial) - I took the advise of Yuri below. Thanks for the direction here. I was able to I have been using this tool and it has brought a lot of insight but I am still stuck on the c# side of the solution. I am an old VB.Net developer though I have done a bunch in c# I am by no means savvy in it and am probably missing something so simple. Again, any insight would be awesome. I can not post the full proto here as stack has char limit on posts. this is the first bit with messages etc. I can post more if it helps but trying to keep it to the important part.
syntax = "proto3";
option csharp_namespace = "SpaceX.API.Device";
package SpaceX.API.Device;
service Device {
//rpc Handle (.SpaceX.API.Device.Request) returns (.SpaceX.API.Device.Response) {}
//rpc Stream (stream .SpaceX.API.Device.ToDevice) returns (stream .SpaceX.API.Device.FromDevice) {}
rpc Handle (Request) returns (Response);
rpc Stream (Request) returns (Response);
}
message ToDevice {
string message = 1;
}
message Request {
uint64 id = 1;
string target_id = 13;
uint64 epoch_id = 14;
oneof request {
SignedData signed_request = 15;
RebootRequest reboot = 1001;
SpeedTestRequest speed_test = 1003;
GetStatusRequest get_status = 1004;
AuthenticateRequest authenticate = 1005;
GetNextIdRequest get_next_id = 1006;
GetHistoryRequest get_history = 1007;
GetDeviceInfoRequest get_device_info = 1008;
GetPingRequest get_ping = 1009;
SetTrustedKeysRequest set_trusted_keys = 1010;
FactoryResetRequest factory_reset = 1011;
GetLogRequest get_log = 1012;
SetSkuRequest set_sku = 1013;
UpdateRequest update = 1014;
GetNetworkInterfacesRequest get_network_interfaces = 1015;
PingHostRequest ping_host = 1016;
GetLocationRequest get_location = 1017;
EnableFlowRequest enable_flow = 1018;
GetHeapDumpRequest get_heap_dump = 1019;
RestartControlRequest restart_control = 1020;
FuseRequest fuse = 1021;
GetPersistentStatsRequest get_persistent_stats = 1022;
GetConnectionsRequest get_connections = 1023;
FlushTelemRequest flush_telem = 1026;
StartSpeedtestRequest start_speedtest = 1027;
GetSpeedtestStatusRequest get_speedtest_status = 1028;
ReportClientSpeedtestRequest report_client_speedtest = 1029;
InitiateRemoteSshRequest initiate_remote_ssh = 1030;
SelfTestRequest self_test = 1031;
SetTestModeRequest set_test_mode = 1032;
DishStowRequest dish_stow = 2002;
DishGetContextRequest dish_get_context = 2003;
DishSetEmcRequest dish_set_emc = 2007;
DishGetObstructionMapRequest dish_get_obstruction_map = 2008;
DishGetEmcRequest dish_get_emc = 2009;
DishSetConfigRequest dish_set_config = 2010;
DishGetConfigRequest dish_get_config = 2011;
StartDishSelfTestRequest start_dish_self_test = 2012;
WifiSetConfigRequest wifi_set_config = 3001;
WifiGetClientsRequest wifi_get_clients = 3002;
WifiSetupRequest wifi_setup = 3003;
WifiGetPingMetricsRequest wifi_get_ping_metrics = 3007;
WifiGetDiagnosticsRequest wifi_get_diagnostics = 3008;
WifiGetConfigRequest wifi_get_config = 3009;
WifiSetMeshDeviceTrustRequest wifi_set_mesh_device_trust = 3012;
WifiSetMeshConfigRequest wifi_set_mesh_config = 3013;
WifiGetClientHistoryRequest wifi_get_client_history = 3015;
TransceiverIFLoopbackTestRequest transceiver_if_loopback_test = 4001;
TransceiverGetStatusRequest transceiver_get_status = 4003;
TransceiverGetTelemetryRequest transceiver_get_telemetry = 4004;
}
reserved 1025, 3011, 3014;
}
message SignedData {
bytes data = 1;
bytes signature = 2;
}
My New .cs
I have tried many things from Microsoft's examples to thing I can gather from other samples. I simply can not get it to work and am lost. Again, any insight would be amazing and hopefully helpful to others looking for a solution in c#. You will see my commented code of this I have been playing with. Basically I am attempting to achieve three things and have made some movement in one of them.
Goals:
1 - Use Server Reflection to discover services.
I think I got this one resolved with dot-net grpc.
2 - Simply want to check available methods under a service and potentially either check or generate a new .proto file in case things change. StaLink does not publish its proto schema so I assume it could change anytime without warning.
3 - Just run any one of the available methods. I have tried the GetDeviceInfoRequest but can not seem to construct the request message properly. I have not been able to get this accomplishe in the gRPCurl tool either. I can do it on the basic service shown by Microsoft of course but these methods seem to be more complex and I simply get all kinds of errors.
Again, any insight or assistance would be amazing. Thanks to any and all in advance.
New .cs File
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Reflection.V1Alpha;
using ServerReflectionClient = Grpc.Reflection.V1Alpha.ServerReflection.ServerReflectionClient;
using SpaceX.API.Device;
public class Program
{
static async Task Main(string[] args)
{
//SETUP CHANNEL AND CLIENT
using var channel = GrpcChannel.ForAddress("http://192.168.100.1:9200");
var client = new ServerReflectionClient(channel);
var StarLinkClient = new Device.DeviceClient(channel);
//using var call = StarLinkClient.StreamAsync(new ToDevice { Request = GetDeviceInfoRequest });
//await foreach (var response in call.ResponseStream.ReadAllAsync())
//var request = Device.GetDeviceInfoRequest;
//var reply = await StarLinkClient.HandleAsync(
// new Request {'"getDeviceInfo" : {} '});
//Console.WriteLine(reply.Message);
//=============================================SERVER REFLECTION=============================================================
Console.WriteLine("Calling reflection service:");
var response = await SingleRequestAsync(client, new ServerReflectionRequest
{
ListServices = "" // Get all services
});
Console.WriteLine("Services:");
foreach (var item in response.ListServicesResponse.Service)
{
Console.WriteLine("- " + item.Name);
Console.WriteLine();
var StarLink = item.Name;
//Console.WriteLine(StarLink.getStatus());
}
//=============================================SERVER REFLECTION=============================================================
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
void setupchannel()
{
}
private static Task SingleRequestAsync(ServerReflectionClient client, Metadata metadata)
{
throw new NotImplementedException();
}
private static async Task<ServerReflectionResponse> SingleRequestAsync(ServerReflectionClient client, ServerReflectionRequest request)
{
using var call = client.ServerReflectionInfo();
await call.RequestStream.WriteAsync(request);
Debug.Assert(await call.ResponseStream.MoveNext());
var response = call.ResponseStream.Current;
await call.RequestStream.CompleteAsync();
return response;
}
}
Again, thanks in advance to anyone willing to assist here. Hopefully this helps others as well.

ICalendar (ICS) - only VEVENT components works good with outlook/googleCalendar

I've got some problems with generating Icalendar files (*.ICS)
Im using library Ical.NET and c# language.
Code is very simple, for example this is VEVENT:
public override void HandleComponent(Ical.Net.Calendar root, CalendarData data)
{
var icalEvent = new Ical.Net.Event();
icalEvent.Start = new CalDateTime(data.Poczatek);
icalEvent.End = new CalDateTime(data.Koniec);
icalEvent.Location = data.Lokalizacja;
icalEvent.Description = data.Opis;
icalEvent.Summary = data.Nazwa;
root.Events.Add(icalEvent);
}
VJOURNEY and VTODO have very similar code <-- firstly i'm creating component, and then add it to calendar object.
Then i have generated file from this code:
var serializer = new CalendarSerializer(calendar);
var icsContent = serializer.SerializeToString();
return icsContent;
and structure of ics files looks like:
BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN
VERSION:2.0
BEGIN:VJOURNAL
ATTENDEE;CN=a;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto:a#wp.pl
ATTENDEE;CN=a2;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto:a2#wp.pl
DESCRIPTION:trele morele
DTSTAMP:20180913T072413Z
DTSTART:20180912T130700
ORGANIZER;CN=Administrator:mailto:Administrator#wp.pl
SEQUENCE:1
SUMMARY:qwerty22
UID:97032fa5-f554-4f10-9c4d-4fdda38148c7
END:VJOURNAL
END:VCALENDAR
just like specification says: https://www.kanzaki.com/docs/ical/vjournal.html
Problem:
Both Outlook 2016, and GoogleCalender handle properly only with VEVENT components on ICalendar file. When i import VJOURNAL or VTODO in GoogleCalendar it responds that he doesn't se any event...
Am i doing something wrong?
I also paste code where i create VJOURNAL
public class CalendarJournalComponent : CalendarComponents
{
public override CalendarComponentType SupportedComponent => CalendarComponentType.Journal;
public override void HandleComponent(Ical.Net.Calendar root, CalendarData data)
{
var journal = new Journal();
journal.Start = new CalDateTime(data.Poczatek);
journal.Description = data.Opis;
journal.Summary = data.Nazwa;
if (data.Prowadzacy.Any())
{
var prowadzacy = data.Prowadzacy.FirstOrDefault();
journal.Organizer = new Organizer() { CommonName = prowadzacy.Value, Value = emailUri(prowadzacy.Key)};
}
journal.Attendees = new List<IAttendee>();
foreach(var uczesnik in data.Uczestnicy)
{
journal.Attendees.Add(new Attendee() { CommonName = uczesnik.Value, Rsvp = true, Value = emailUri(uczesnik.Key), Role = "REQ-PARTICIPANT" });
}
root.Journals.Add(journal);
}
private Func<string, Uri> emailUri = x => new Uri(String.Format("mailto:{0}", x));
}
and VTODO compoment:
public class CalendarTodoComponent : CalendarComponents
{
public override CalendarComponentType SupportedComponent => CalendarComponentType.ToDo;
public override void HandleComponent(Ical.Net.Calendar root, CalendarData data)
{
var todo = new Todo();
todo.Start = new CalDateTime(data.Poczatek);
todo.Description = data.Opis;
todo.Summary = data.Nazwa;
todo.Location = data.Lokalizacja;
root.Todos.Add(todo);
}
}
Your code is most likely fine. But you need to ask yourself what you really expect to do by trying to import those VJOURNAL and VTODO in Google Calendar ?
As of today, Google Calendar really only support events/meetings:
It does not support VJOURNAL. As a matter of fact, there are really few software which do support VJOURNAL. From its definition (https://www.rfc-editor.org/rfc/rfc5545#section-3.6.3) you will see that it could be seen as the ancestor of a blog entry.
It does not support VTODO either. The closest you could find would be in Gmail which has a notion of Tasks list.

Sabre Web Services .NET API Examples that aren't MVC?

This is pretty general, but I'm having a hell of a time figuring out how to consume some of the more complicated Sabre APIs.
I have built working .NET proxy classes in C# using the WSDL for the basic APIs (CreateSession, CloseSession) but for the more complicated APIs I have a really hard time parsing out the complicated XML schema to figure out which methods to call in my program.
Are there any other .NET resources/examples out there that aren't wrapped up in MVC like the code example that Sabre posted on GitHub?
I'm trying to figure out how to use APIs like OTA_AirPriceLLSRQ and TravelItineraryReadRQ.
Thanks in advance for any help!
As I mentioned on the comments, you should not focus on the actual MVC wrapping, as you'll be mainly putting stuff in the Model, or actually you'll put this somewhere else and consume it in the model.
Anyway, just for you to have as example, here's a VERY generic BFM (BargianFinderMax) class. With this approach it's required to create an instance, and after calling the Execute method it stores the response in the instance.
I hope it helps.
using BargainFinderMaxRQv310Srvc;
using System;
using System.IO;
namespace ServicesMethods
{
public class BFM_v310
{
private BargainFinderMaxService service;
private OTA_AirLowFareSearchRQ request;
public OTA_AirLowFareSearchRS response;
public BFM_v310(string token, string pcc, string convId, string endpoint)
{
//MessageHeader
MessageHeader mHeader = new MessageHeader();
PartyId[] pId = { new PartyId() };
pId[0].Value = "SWS";
From from = new From();
from.PartyId = pId;
To to = new To();
to.PartyId = pId;
mHeader.Action = "BargainFinderMaxRQ";
mHeader.Service = new Service()
{
Value = mHeader.Action
};
mHeader.ConversationId = convId;
mHeader.CPAId = pcc;
mHeader.From = from;
mHeader.To = to;
mHeader.MessageData = new MessageData()
{
Timestamp = DateTime.UtcNow.ToString()
};
//Security
Security security = new Security();
security.BinarySecurityToken = token;
//Service
service = new BargainFinderMaxService();
service.MessageHeaderValue = mHeader;
service.SecurityValue = security;
service.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap11;
service.Url = endpoint;
createRequest(pcc);
}
private void createRequest(string pcc)
{
request = new BargainFinderMaxRQv310Srvc.OTA_AirLowFareSearchRQ();
request.AvailableFlightsOnly = true;
request.Version = "3.1.0";
request.POS = new SourceType[1];
SourceType source = new SourceType();
source.PseudoCityCode = pcc;
source.RequestorID = new UniqueID_Type();
source.RequestorID.ID = "1";
source.RequestorID.Type = "1";
source.RequestorID.CompanyName = new CompanyNameType();
source.RequestorID.CompanyName.Code = "TN";
source.RequestorID.CompanyName.CodeContext = "Context";
request.POS[0] = source;
OTA_AirLowFareSearchRQOriginDestinationInformation originDestination = new OTA_AirLowFareSearchRQOriginDestinationInformation();
originDestination.OriginLocation = new OriginDestinationInformationTypeOriginLocation();
originDestination.OriginLocation.LocationCode = "BCN";
originDestination.DestinationLocation = new OriginDestinationInformationTypeDestinationLocation();
originDestination.DestinationLocation.LocationCode = "MAD";
originDestination.ItemElementName = ItemChoiceType.DepartureDateTime;
originDestination.Item = "2017-09-10T12:00:00";
originDestination.RPH = "1";
request.OriginDestinationInformation = new OTA_AirLowFareSearchRQOriginDestinationInformation[1] { originDestination };
request.TravelerInfoSummary = new TravelerInfoSummaryType()
{
AirTravelerAvail = new TravelerInformationType[1]
};
request.TravelerInfoSummary.AirTravelerAvail[0] = new TravelerInformationType()
{
PassengerTypeQuantity = new PassengerTypeQuantityType[1]
};
PassengerTypeQuantityType passenger = new PassengerTypeQuantityType()
{
Quantity = "1",
Code = "ADT"
};
request.TravelerInfoSummary.AirTravelerAvail[0].PassengerTypeQuantity[0] = passenger;
request.TravelerInfoSummary.PriceRequestInformation = new PriceRequestInformationType();
request.TravelerInfoSummary.PriceRequestInformation.CurrencyCode = "USD";
//PriceRequestInformationTypeNegotiatedFareCode nego = new PriceRequestInformationTypeNegotiatedFareCode();
//nego.Code = "ABC";
//request.TravelerInfoSummary.PriceRequestInformation.Items = new object[1] { nego };
request.TPA_Extensions = new OTA_AirLowFareSearchRQTPA_Extensions();
request.TPA_Extensions.IntelliSellTransaction = new TransactionType();
request.TPA_Extensions.IntelliSellTransaction.RequestType = new TransactionTypeRequestType();
request.TPA_Extensions.IntelliSellTransaction.RequestType.Name = "50ITIN";
}
public bool Execute()
{
response = service.BargainFinderMaxRQ(request);
return response.PricedItinCount > 0;
}
}
}
My advice is you should add separate models which are built based on Sabre models, and which flatten the whole structure.
For example, TravelItineraryReadRS is a quite complicated document. Using it properties in your program is a real "pain", because every time you need to remember the whole path that leads to to specific information (like, "what is passenger type for PersonName of NameNumber 01.01?").
I suggest you have dedicated model (let's name it Reservation), which have all information that you will need later in your application, extracted from TravelItineraryReadRs.
In order to achieve this you need a dedicated converter which will make convert TravelItineraryReadRs model into Reservation model. Now, inside Reservation model you could have list of Passenger models, which have in on place all important information (NameNumber, PassengerType, SSR codes, etc).
This improves readability and as a bonus you decouple your application from Sabre (imagine, one day someone asks "can we switch from Sabre to Amadeus?" - if you use dedicated models the answer is "yes". If you don't have, then the answer is "probably yes, but it will take 6-9 months).

How to create New EPT by using CSOM

I try to create a new EPT (project server 2013) using C# CSOM library.
But It has following error occurred.
"PJClientCallableException: EnterpriseProjectTypeInvalidCreatePDPUid"
Couple of article tell to change the "IsCreate=true". But it does not success for me. Here is the code what I have done.
public void CreateEnterpriseProjectType(Guid eptGuid, string eptName, string eptDescription)
{
ProjectContext pwaContext = new ProjectContext(this.PWA_URL);
EnterpriseProjectTypeCreationInformation eptData = new EnterpriseProjectTypeCreationInformation();
eptData.Id = eptGuid;
eptData.Name = eptName;
eptData.Description = eptDescription;
eptData.IsDefault = false;
eptData.IsManaged = true;
eptData.WorkspaceTemplateName = "PROJECTSITE#0";
eptData.ProjectPlanTemplateId = Guid.Empty;
eptData.WorkflowAssociationId = Guid.Empty;
eptData.Order = 4;
List<ProjectDetailPageCreationInformation> projectDetailPages = new
List<ProjectDetailPageCreationInformation>() {
new ProjectDetailPageCreationInformation() {
Id = pwaContext.ProjectDetailPages[1].Id, IsCreate = true }
};
eptData.ProjectDetailPages = projectDetailPages;
pwaContext.Load(pwaContext.EnterpriseProjectTypes);
pwaContext.ExecuteQuery();
EnterpriseProjectType newEpt = pwaContext.EnterpriseProjectTypes.Add(eptData);
pwaContext.EnterpriseProjectTypes.Update();
pwaContext.ExecuteQuery();
}
Can anyone explain the issue or provide the working code part.
I would like to suggest the following:
Define an enterprise project type:
string basicEpt = "Enterprise Project"; // Basic enterprise project type.
int timeoutSeconds = 10; // The maximum wait time for a queue job, in seconds.
And then, when you create the new project, work like this:
ProjectCreationInformation newProj = new ProjectCreationInformation();
newProj.Id = Guid.NewGuid();
newProj.Name = "Project Name";
newProj.Description = "Test creating a project with CSOM";
newProj.Start = DateTime.Today.Date;
// Setting the EPT GUID is optional. If no EPT is specified, Project Server
// uses the default EPT.
newProj.EnterpriseProjectTypeId = GetEptUid(basicEpt);
PublishedProject newPublishedProj = projContext.Projects.Add(newProj);
QueueJob qJob = projContext.Projects.Update();
// Calling Load and ExecuteQuery for the queue job is optional.
// projContext.Load(qJob);
// projContext.ExecuteQuery();
JobState jobState = projContext.WaitForQueue(qJob, timeoutSeconds);
When the last line of that piece of code ends, the project must be created and published in order to define tasks or whatever.
I don't know what is happening to your code, seems great.
Hope it helps to you,

Analysing C# source with Irony

This is what my team and I chose to do for our school project. Well, actually we haven't decided on how to parse the C# source files yet.
What we are aiming to achieve is, perform a full analysis on a C# source file, and produce up a report.
In which the report is going to contain stuff that happening in the codes.
The report only has to contain:
string literals
method names
variable names
field names
etc
I'm in charge of looking into this Irony library. To be honest, I don't know the best way to sort the data out into a clean readable report. I am using the C# grammar class packed with the zip.
Is there any step where I can properly identify each node children? (eg: using directives, namespace declaration, class declaration etc, method body)
Any help or advice would be very much appreciated. Thanks.
EDIT: Sorry I forgot to say we need to analysis the method calls too.
Your main goal is to master the basics of formal languages. A good start-up might be found here. This article describes the way to use Irony on the sample of a grammar of a simple numeric calculator.
Suppose you want to parse a certain file containing C# code the path to which you know:
private void ParseForLongMethods(string path)
{
_parser = new Parser(new CSharpGrammar());
if (_parser == null || !_parser.Language.CanParse()) return;
_parseTree = null;
GC.Collect(); //to avoid disruption of perf times with occasional collections
_parser.Context.SetOption(ParseOptions.TraceParser, true);
try
{
string contents = File.ReadAllText(path);
_parser.Parse(contents);//, "<source>");
}
catch (Exception ex)
{
}
finally
{
_parseTree = _parser.Context.CurrentParseTree;
TraverseParseTree();
}
}
And here is the traversal method itself with counting some info in the nodes. Actually this code counts the number of statements in every method of the class. If you have any question you are always welcome to ask me
private void TraverseParseTree()
{
if (_parseTree == null) return;
ParseNodeRec(_parseTree.Root);
}
private void ParseNodeRec(ParseTreeNode node)
{
if (node == null) return;
string functionName = "";
if (node.ToString().CompareTo("class_declaration") == 0)
{
ParseTreeNode tmpNode = node.ChildNodes[2];
currentClass = tmpNode.AstNode.ToString();
}
if (node.ToString().CompareTo("method_declaration") == 0)
{
foreach (var child in node.ChildNodes)
{
if (child.ToString().CompareTo("qual_name_with_targs") == 0)
{
ParseTreeNode tmpNode = child.ChildNodes[0];
while (tmpNode.ChildNodes.Count != 0)
{ tmpNode = tmpNode.ChildNodes[0]; }
functionName = tmpNode.AstNode.ToString();
}
if (child.ToString().CompareTo("method_body") == 0) //method_declaration
{
int statementsCount = FindStatements(child);
//Register bad smell
if (statementsCount>(((LongMethodsOptions)this.Options).MaxMethodLength))
{
//function.StartPoint.Line
int functionLine = GetLine(functionName);
foundSmells.Add(new BadSmellRegistry(name, functionLine,currentFile,currentProject,currentSolution,false));
}
}
}
}
foreach (var child in node.ChildNodes)
{ ParseNodeRec(child); }
}
I'm not sure this is what you need but you could use the CodeDom and CodeDom.Compiler namespaces to compile the C# code, and than analyze the results using Reflection, something like:
// Create assamblly in Memory
CodeSnippetCompileUnit code = new CodeSnippetCompileUnit(classCode);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromDom(compileParams, code);
foreach(var type in results.CompiledAssembly)
{
// Your analysis go here
}
Update: In VS2015 you could use the new C# compiler (AKA Roslyn) to do the same, for example:
var root = (CompilationUnitSyntax)tree.GetRoot();
var compilation = CSharpCompilation.Create("HelloTDN")
.AddReferences(references: new[] { MetadataReference.CreateFromAssembly(typeof(object).Assembly) })
.AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
var nameInfo = model.GetSymbolInfo(root.Usings[0].Name);
var systemSymbol = (INamespaceSymbol)nameInfo.Symbol;
foreach (var ns in systemSymbol.GetNamespaceMembers())
{
Console.WriteLine(ns.Name);
}

Categories