I have tried to create XML log file using log4net and it create log successfully but without root element
my question how to create the root element
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="logger.xml" type="log4net.Util.PatternString" />
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="20"/>
<maximumFileSize value="10000KB"/>
<staticLogFileName value="true"/>
<preserveLogFileNameExtension value="true" />
<layout type="Log4net1.CustomXmlLayoutSchemaLog4j">
<locationInfo value="true" />
</layout>
</appender>
<event logger="SomeName2" time="4/16/2019 2:45:15 PM" level="ERROR" thread="1"><message>Exception Caught: </message><properties><data name="log4jmachinename" value="IbrahimS-LPT" /><data name="log4japp" value="Log4net.exe" /><data name="log4net:Identity" value="" /><data name="log4net:UserName" value="SSS-PROCESS\ibrahims" /><data name="log4net:HostName" value="IbrahimS-LPT" /></properties><throwable>System.InvalidCastException: hhhhh ---> System.Exception: sub
--- End of inner exception stack trace ---</throwable><locationInfo class="Log4net.LoggingHelper" method="LogError" file="" line="0" /></event>
<event logger="SomeName2" time="4/16/2019 2:45:15 PM" level="ERROR" thread="1"><message>Exception Caught: </message><properties><data name="log4jmachinename" value="IbrahimS-LPT" /><data name="log4japp" value="Log4net.exe" /><data name="log4net:Identity" value="" /><data name="log4net:UserName" value="SSS-PROCESS\ibrahims" /><data name="log4net:HostName" value="IbrahimS-LPT" /></properties><throwable>System.InvalidCastException: hhhhh ---> System.Exception: sub
--- End of inner exception stack trace ---</throwable><locationInfo class="Log4net.LoggingHelper" method="LogError" file="" line="0" /></event>
when trying to open the file in xml format I got this error Unable to parse any XML input
but when add root element manually i am able to view
i need to be like this
<app>
<event logger="SomeName2" time="4/16/2019 2:45:15 PM" level="ERROR" thread="1"></event>
<event logger="SomeName2" time="4/16/2019 2:45:15 PM" level="ERROR" thread="1"></event>
</app>
class CustomXmlLayoutSchemaLog4j : XmlLayoutBase
{
#region Static Members
/// <summary>
/// The 1st of January 1970 in UTC
/// </summary>
private static readonly DateTime s_date1970 = new DateTime(1970, 1, 1);
#endregion
#region Constructors
/// <summary>
/// Constructs an XMLLayoutSchemaLog4j
/// </summary>
public CustomXmlLayoutSchemaLog4j() : base()
{
}
/// <summary>
/// Constructs an XMLLayoutSchemaLog4j.
/// </summary>
/// <remarks>
/// <para>
/// The <b>LocationInfo</b> option takes a boolean value. By
/// default, it is set to false which means there will be no location
/// information output by this layout. If the the option is set to
/// true, then the file name and line number of the statement
/// at the origin of the log statement will be output.
/// </para>
/// <para>
/// If you are embedding this layout within an SMTPAppender
/// then make sure to set the <b>LocationInfo</b> option of that
/// appender as well.
/// </para>
/// </remarks>
public CustomXmlLayoutSchemaLog4j(bool locationInfo) : base(locationInfo)
{
}
#endregion
#region Public Properties
/// <summary>
/// The version of the log4j schema to use.
/// </summary>
/// <remarks>
/// <para>
/// Only version 1.2 of the log4j schema is supported.
/// </para>
/// </remarks>
public string Version
{
get { return "1.2"; }
set
{
if (value != "1.2")
{
throw new ArgumentException("Only version 1.2 of the log4j schema is currently supported");
}
}
}
#endregion
/* Example log4j schema event
<event logger="first logger" level="ERROR" thread="Thread-3" timestamp="1051494121460">
<message><![CDATA[errormsg 3]]></message>
<NDC><![CDATA[third]]></NDC>
<MDC>
<data name="some string" value="some valuethird"/>
</MDC>
<throwable><![CDATA[java.lang.Exception: someexception-third
at org.apache.log4j.chainsaw.Generator.run(Generator.java:94)
]]></throwable>
<locationInfo class="org.apache.log4j.chainsaw.Generator"
method="run" file="Generator.java" line="94"/>
<properties>
<data name="log4jmachinename" value="windows"/>
<data name="log4japp" value="udp-generator"/>
</properties>
</event>
*/
/* Since log4j 1.3 the MDC has been combined into the properties element */
/// <summary>
/// Actually do the writing of the xml
/// </summary>
/// <param name="writer">the writer to use</param>
/// <param name="loggingEvent">the event to write</param>
/// <remarks>
/// <para>
/// Generate XML that is compatible with the log4j schema.
/// </para>
/// </remarks>
override protected void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
// Translate logging events for log4j
Header = "App";
// Translate hostname property
if (loggingEvent.LookupProperty(LoggingEvent.HostNameProperty) != null &&
loggingEvent.LookupProperty("log4jmachinename") == null)
{
loggingEvent.GetProperties()["log4jmachinename"] = loggingEvent.LookupProperty(LoggingEvent.HostNameProperty);
}
// translate appdomain name
if (loggingEvent.LookupProperty("log4japp") == null &&
loggingEvent.Domain != null &&
loggingEvent.Domain.Length > 0)
{
loggingEvent.GetProperties()["log4japp"] = loggingEvent.Domain;
}
// translate identity name
if (loggingEvent.Identity != null &&
loggingEvent.Identity.Length > 0 &&
loggingEvent.LookupProperty(LoggingEvent.IdentityProperty) == null)
{
loggingEvent.GetProperties()[LoggingEvent.IdentityProperty] = loggingEvent.Identity;
}
// translate user name
if (loggingEvent.UserName != null &&
loggingEvent.UserName.Length > 0 &&
loggingEvent.LookupProperty(LoggingEvent.UserNameProperty) == null)
{
loggingEvent.GetProperties()[LoggingEvent.UserNameProperty] = loggingEvent.UserName;
}
// Write the start element
writer.WriteStartElement("event");
writer.WriteAttributeString("logger", loggingEvent.LoggerName);
// Calculate the timestamp as the number of milliseconds since january 1970
//
// We must convert the TimeStamp to UTC before performing any mathematical
// operations. This allows use to take into account discontinuities
// caused by daylight savings time transitions.
TimeSpan timeSince1970 = loggingEvent.TimeStampUtc - s_date1970;
writer.WriteAttributeString("time", DateTime.Now.ToString());
writer.WriteAttributeString("level", loggingEvent.Level.DisplayName);
writer.WriteAttributeString("thread", loggingEvent.ThreadName);
// Append the message text
writer.WriteStartElement("message");
Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage, this.InvalidCharReplacement);
writer.WriteEndElement();
object ndcObj = loggingEvent.LookupProperty("NDC");
if (ndcObj != null)
{
string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(ndcObj);
if (valueStr != null && valueStr.Length > 0)
{
// Append the NDC text
writer.WriteStartElement("NDC");
Transform.WriteEscapedXmlString(writer, valueStr, this.InvalidCharReplacement);
writer.WriteEndElement();
}
}
// Append the properties text
PropertiesDictionary properties = loggingEvent.GetProperties();
if (properties.Count > 0)
{
writer.WriteStartElement("properties");
foreach (System.Collections.DictionaryEntry entry in properties)
{
writer.WriteStartElement("data");
writer.WriteAttributeString("name", (string)entry.Key);
// Use an ObjectRenderer to convert the object to a string
string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
writer.WriteAttributeString("value", valueStr);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
string exceptionStr = loggingEvent.GetExceptionString();
if (exceptionStr != null && exceptionStr.Length > 0)
{
// Append the stack trace line
writer.WriteStartElement("throwable");
Transform.WriteEscapedXmlString(writer, exceptionStr, this.InvalidCharReplacement);
writer.WriteEndElement();
}
if (LocationInfo)
{
LocationInfo locationInfo = loggingEvent.LocationInformation;
writer.WriteStartElement("locationInfo");
writer.WriteAttributeString("class", locationInfo.ClassName);
writer.WriteAttributeString("method", locationInfo.MethodName);
writer.WriteAttributeString("file", locationInfo.FileName);
writer.WriteAttributeString("line", locationInfo.LineNumber);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
What you need to do is the next:
before this part of code:
// Write the start element
writer.WriteStartElement("event");
writer.WriteAttributeString("logger", loggingEvent.LoggerName);
You'll need to add this line:
writer.WriteStartElement("App");
Also, add a writer.WriteEndElement(); after the last one in you current code.
So your code will look like:
// Write the start element
writer.WriteStartElement("App");
writer.WriteStartElement("event");
writer.WriteAttributeString("logger", loggingEvent.LoggerName);
.
.
.
.
writer.WriteEndElement();
writer.WriteEndElement();
Related
I have added this xml doc to my method:
/// <summary>
/// Create an <see cref="ArgumentOutOfRangeException" /> with the message<br />
/// "The value '<paramref name="i_value" />' is out of the range of '<paramref name="i_range" />.<see cref="IRange{T}.LowerValue"/>' to '<paramref name="i_range" />.<see cref="IRange{T}.UpperValue"/>'."
/// </summary>
public static Exception Create_ArgumentOutOfRange<T> (string? i_prefix,
object i_value,
IRange<T> i_range,
string? i_suffix = null,
Exception? i_innerException = null)
and the xml doc is shown as
which is quite ugly and difficult to read.
Is there a way of leaving out "IRange<out T>" of the message, but still referring to the code element?
If I used the property on the argument directly:
/// "The value '<paramref name="i_value" />' is out of the range of '<paramref name="i_range.LowerValue" />' to '<paramref name="i_range.UpperValue" />'."
then I get
which is not that ugly, but the property name is written twice, and thus wrong and misleading.
I wish to replace a TextBox, with a webbrowser to display messages in chat app
So I can add hyperlinks to videos, sites and pics, emoticons ...
But When I run the app, it cannot find the myfile.html which is in Debug folder ...
It does exists and loadable in firefox
List<string> lines = new List<string>();
lines.Add("<html>");
lines.Add("<body><table width=100%><br><br>Chat:");
lines.Add("<tr><font size=6><a href=music\\" + artist + ">
<img src=music\\" + artist + "\\info\\default.jpg alt=" + art + " height=200 width=200 />
</a>"+" " + path2 +"</font><br><br></tr>");
lines.Add("</table></body>");
lines.Add("</html>");
File.WriteAllLines("myfile.html", lines);
wb_Messages.Navigate("myfile.html");
I have tried
wb_Messages.Navigate("file:///myfile.html");
wb_Messages.Navigate(Application.StartupPath + #"myfile.html");
No Joy ...
Any Help Thanks ...
i can't use comments for this but could you try to use the following helpers i dug up from my project(s):
/// <summary>
/// Helpers for working with <see cref="System.IO.Path"/> or working with paths in general.
/// </summary>
public static class PathHelpers
{
/// <summary>
/// The default output directory, based on the on the currently executing <see cref="System.Reflection.Assembly"/>
/// </summary>
public static string CurrentOutPutDirectory => Path.GetDirectoryName(AssemblyHelper.ExecutableAssembly.GetName().CodeBase)?.Substring(6);
public static string CurrentOutPutDirectoryRoot => Path.GetPathRoot(CurrentOutPutDirectory);
/// <summary>
/// Checks if a string could be used in a path
/// <see href="https://stackoverflow.com/questions/2435894/net-how-do-i-check-for-illegal-characters-in-a-path#comment30899588_2435894"/>
/// </summary>
/// <param name="path">Path to check</param>
/// <returns></returns>
public static bool FilePathHasInvalidChars(string path)
{
return (!string.IsNullOrEmpty(path) && path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0 && path.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) > 0);
}
}
/// <summary>
/// Helpers for dealing with <see cref="System.Reflection.Assembly"/>
/// </summary>
public static class AssemblyHelper
{
/// <summary>
/// The assembly that holds the executable. For this, <see cref="Assembly.GetEntryAssembly()"/> is used.
/// This looks at the current <see cref="AppDomain"/>.
/// <warn>A fallback value is used to get the executing assembly!</warn>
/// </summary>
public static Assembly ExecutableAssembly => Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
}
Then use var alltext = File.ReadAllText(Path.Combine(PathHelpers.CurrentOutPutDirectory, "YourFileIntheDebugFolder.txt)) (pseudo code) to check if it works and loads the html directly.
EDIT to display a html file in the webbrowser, use this top answer, and pass the alltext string.
https://stackoverflow.com/a/20351048/4122889
I think it's because you use only file name instead of full path.
File.WriteAllLines("myfile.html", lines);
In this method you didn't specify full path, so it will try to save file in some default location. What location? No need to think about it now.
wb_Messages.Navigate("myfile.html");
This method tries to read data from file, but only file name is specified. It doesn't know where to find this file, so it will look in some default location also.
But default locations for both methods can be different. I don't know how it works internally, but maybe you will get some info in Microsoft documentation.
Simple solution:
// now filePath becomes "C:\something...\myfile.html"
string filePath = Path.GetFullPath("myfile.html");
// save data using full path
File.WriteAllLines(filePath, lines);
// navigate to file using full path
wb_Messages.Navigate(filePath);
Another Microsoft CRM plugin related question. My plugin is firing when an invoice record is updated. I want to copy the contents of some of the invoice fields to the corresponding fields on the commission record. The if which contains the due date seems to work ok but the if containing the invoiceamount gives a key not present error. I have checked that all the field names are correct and exist in the correct entities.
Any ideas what's happening? Do I need to use images and if so, could somebody help me with some code, please?
Thanks
// <copyright file="PreInvoiceUpdate.cs" company="">
// Copyright (c) 2013 All Rights Reserved
// </copyright>
// <author></author>
// <date>8/8/2013 10:40:22 AM</date>
// <summary>Implements the PreInvoiceUpdate Plugin.</summary>
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.1
// </auto-generated>
namespace UpdateCommission
{
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// PreInvoiceUpdate Plugin.
/// Fires when the following attributes are updated:
/// All Attributes
/// </summary>
public class PreInvoiceUpdate : Plugin
{
/// <summary>
/// Initializes a new instance of the <see cref="PreInvoiceUpdate"/> class.
/// </summary>
public PreInvoiceUpdate()
: base(typeof(PreInvoiceUpdate))
{
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Update", "invoice", new Action<LocalPluginContext>(ExecutePreInvoiceUpdate)));
// Note : you can register for more events here if this plugin is not specific to an individual entity and message combination.
// You may also need to update your RegisterFile.crmregister plug-in registration file to reflect any change.
}
/// <summary>
/// Executes the plug-in.
/// </summary>
/// <param name="localContext">The <see cref="LocalPluginContext"/> which contains the
/// <see cref="IPluginExecutionContext"/>,
/// <see cref="IOrganizationService"/>
/// and <see cref="ITracingService"/>
/// </param>
/// <remarks>
/// For improved performance, Microsoft Dynamics CRM caches plug-in instances.
/// The plug-in's Execute method should be written to be stateless as the constructor
/// is not called for every invocation of the plug-in. Also, multiple system threads
/// could execute the plug-in at the same time. All per invocation state information
/// is stored in the context. This means that you should not use global variables in plug-ins.
/// </remarks>
protected void ExecutePreInvoiceUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
// TODO: Implement your custom Plug-in business logic.
// Obtain the execution context from the service provider.
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
IServiceProvider serviceProvider = localContext.ServiceProvider;
ITracingService tracingService = localContext.TracingService;
// Obtain the target entity from the input parmameters.
//Entity contextEntity = (Entity)context.InputParameters["Target"];
if (context.Depth == 1)
{
#region Set up variables
Entity targetInvoice = null;
targetInvoice = (Entity)context.InputParameters["Target"];
Guid invoiceID = targetInvoice.Id;
ColumnSet invoiceCols = new ColumnSet("new_totalcomm", "new_grossprofit", "new_invoiceamount", "duedate");
//Entity contact = service.Retrieve("contact", cid, cols);
//contact.Attributes["jobtitle"] = "Sometitle";
// contact.Attributes["address1_line1"] = "Some address";
//service.Update(contact);
string fetchCommissionQuery = #"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='tbw_commission'>
<attribute name='tbw_commissionid' />
<attribute name='tbw_name' />
<attribute name='createdon' />
<order attribute='tbw_name' descending='false' />
<filter type='and'>
<condition attribute='new_relatedinvoice' operator='eq' uitype='invoice' value='";
fetchCommissionQuery += invoiceID;
fetchCommissionQuery += #"' />
</filter>
</entity>
</fetch> ";
EntityCollection commissionCollection = service.RetrieveMultiple(new FetchExpression(fetchCommissionQuery));
//targetInvoice = service.Retrieve("invoice", invoiceID, invoiceCols);
#endregion
foreach (var commission in commissionCollection.Entities)
{
if (targetInvoice.Attributes["duedate"] != null)
{
commission.Attributes["tbw_duedate"] = targetInvoice.Attributes["duedate"];
}
if (targetInvoice.Attributes["new_invoiceamount"] != null)// && commission.Attributes["tbw_paymentrate"] != null)
{
//dynamic commRate = commission.Attributes["tbw_paymentrate"];
//dynamic invoiceTotal = ((Money)targetInvoice.Attributes["new_invoiceamount"]).Value;
//dynamic commTotal = commRate * invoiceTotal;
//commission.Attributes["tbw_total"] = new Money(commTotal);
//commission.Attributes["tbw_total"] = 1.02;
}
//commission.Attributes["tbw_name"] = "TestCommName";
service.Update(commission);
}
// dynamic currentInvoiceTotal = ((Money)targetInvoice.Attributes["new_invoiceamount"]).Value;
// currentInvoiceTotal = currentInvoiceTotal - 10;
// Entity invoice = service.Retrieve("invoice", invoiceID, invoiceCols);
// invoice.Attributes["new_grossprofit"] = new Money(currentInvoiceTotal);
//service.Update(invoice);
//}
}
}
}
}
In Update, you will only have the value of fields those are updated. Other fields won't be there in Target Entity. You need to register plugin with PreImage. Add the fields to PreImage you want to access in plugin.
preImage = (Entity)context.PreEntityImages["PreImage"];
How to Register Plugin with Image
I'm looking for a way to programmatically get the summary portion of Xml-comments of a method in ASP.net.
I have looked at the previous related posts and they do not supply a way of doing so in a web environment.
I can not use any 3rd party apps and due to a web environment, Visual studio plugin's aren't much use either.
The closest thing I have found to a working solution was the JimBlackler project, but it only works on DLL's.
Naturally, something like 'supply .CS file, get XML documentation' would be optimal.
Current situation
I have a web-service and trying to dynamically generate documentation for it.
Reading the Methods, and properties is easy, but getting the Summary for each method is throwing me off a bit.
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public void SomeMethod()
{
}
}
A Workaround - Using reflection on Program.DLL/EXE together with Program.XML file
If you take a look at the sibling .XML file generated by Visual Studio you will see that there is a fairly flat hierarchy of /members/member.
All you have to do is get hold on each method from your DLL via MethodInfo object. Once you have this object you turn to the XML and use XPATH to get the member containing the XML documentation for this method.
Members are preceded by a letter. XML doc for methods are preceded by "M:" for class by "T:" etc.
Load your sibling XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
Use this xpath to get the member representing the method XML docu
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(#name, '" + path + "')]");
Now scan childnodes for all the rows of "///"
Sometimes the /// Summary contains extra blanks, if this bothers use this to remove
var cleanStr = Regex.Replace(row.InnerXml, #"\s+", " ");
The XML summary isn't stored in the .NET assembly - it's optionally written out to an XML file as part of your build (assuming you're using Visual Studio).
Consequently there is no way to "pull out" the XML summaries of each method via reflection on a compiled .NET assembly (either .EXE or .DLL) - because the data simply isn't there for you to pull out. If you want the data, you'll have to instruct your build environment to output the XML files as part of your build process and parse those XML files at runtime to get at the summary information.
You could 'document' your method using the System.ComponentModel.DataAnnotations.DisplayAttribute attribute, e.g.
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
then use reflection to pull the description at runtime.
A deleted post, made by #OleksandrIeremenko, on this thread links to this article https://jimblackler.net/blog/?p=49 which was the basis for my solution.
Below is a modification of Jim Blackler's code making extension methods off the MemberInfo and Type objects and adding code that returns the summary text or an empty string if not available.
Usage
var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Extension Class
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement GetDocumentation(this MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
var parametersString = "";
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
}
parametersString += parameterInfo.ParameterType.FullName;
}
//AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
if (parametersString.Length > 0)
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
}
/// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement GetDocumentation(this MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
}
/// <summary>
/// Returns the Xml documenation summary comment for this member
/// </summary>
/// <param name="memberInfo"></param>
/// <returns></returns>
public static string GetSummary(this MemberInfo memberInfo)
{
var element = memberInfo.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement GetDocumentation(this Type type)
{
// Prefix in type names is T
return XmlFromName(type, 'T', "");
}
/// <summary>
/// Gets the summary portion of a type's documenation or returns an empty string if not available
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetSummary(this Type type)
{
var element = type.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XmlFromName(this Type type, char prefix, string name)
{
string fullName;
if (string.IsNullOrEmpty(name))
fullName = prefix + ":" + type.FullName;
else
fullName = prefix + ":" + type.FullName + "." + name;
var xmlDocument = XmlFromAssembly(type.Assembly);
var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[#name='" + fullName + "']") as XmlElement;
return matchedElement;
}
/// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();
/// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();
/// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XmlFromAssembly(this Assembly assembly)
{
if (FailCache.ContainsKey(assembly))
{
throw FailCache[assembly];
}
try
{
if (!Cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
Cache[assembly] = XmlFromAssemblyNonCached(assembly);
}
return Cache[assembly];
}
catch (Exception exception)
{
FailCache[assembly] = exception;
throw;
}
}
/// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
{
var assemblyFilename = assembly.Location;
if (!string.IsNullOrEmpty(assemblyFilename))
{
StreamReader streamReader;
try
{
streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
}
catch (FileNotFoundException exception)
{
throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
}
var xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader);
return xmlDocument;
}
else
{
throw new Exception("Could not ascertain assembly filename", null);
}
}
}
You can use Namotion.Reflection NuGet package to get these information:
string summary = typeof(Foo).GetXmlDocsSummary();
You can look at https://github.com/NSwag/NSwag - source for nuget NSwag.CodeGeneration - it gets summary as well, usage
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
(try ILSPY decompiler against your dll, you check code and comments)
If you have access to the source code you're trying to get comments for, then you can use Roslyn compiler platform to do that. It basically gives you access to all the intermediary compiler metadata and you can do anything you want with it.
It's a bit more complicated than what other people are suggesting, but depending on what your needs are, might be an option.
It looks like this post has a code sample for something similar.
i have a Question i want to add Child Element in My Existing Xml How Can i Do this
please help me
Simple:
Load the XML into memory
Find the existing node where you want to append
Create the new element
Call something like XNode.AddAfterSelf
Save the result
The exact calls will depend on which library you use; personally I'd suggest using LINQ to XML if you possibly can (i.e. if you're using .NET 3.5 or higher) as it's much easier to use than the earlier APIs.
I'm using LINQ-to-XML, this seems easier to me and that is how I do it
First load it
/// <summary>
/// loads and returns the XML file with the given name
/// </summary>
/// <param name="modelHesapAdi"> name of the XML file to be returned</param>
/// <returns>returns the xml of given model hesap adı</returns>
public static XElement LoadXMLWithGivenModelHesapAdi(string modelHesapAdi, string xmlDirectory)
{
XElement modelsXmlFile = XElement.Load(xmlDirectory + modelHesapAdi + ".xml");
return modelsXmlFile;
}
Call the above method in another one
/// <summary>
/// gets a roommessage nood from CreateRoomMessageXElement
/// and adds it to the related room XML file and saves it
/// </summary>
/// <param name="modelHesapAdi">a string which has the name of the XML file to be changed</param>
/// <param name="incomingMemberHesapAdi">a string to be inserted to the xml file, which has the members name</param>
/// <param name="entranceTime"> a string for time, holds the member's entrance time</param>
public void AddMemberNodeToRoomMembersXMLWithGivenModelHesapAdiAndUyeHesapAdi(string modelHesapAdi,
string incomingMemberHesapAdi,
string entranceTime)
{
XElement modelsXmlFile = BAL.Models.Model.LoadXMLWithGivenModelHesapAdi(modelHesapAdi, xmlDirectory);//loads the xml
XElement roomMember = CreateRoomIncomingMemberXElement(incomingMemberHesapAdi, entranceTime);//creates child element and returns it
modelsXmlFile.Add(roomMember);//adds the child element
modelsXmlFile.Save(xmlDirectory + modelHesapAdi + ".xml");//saves the edited file
}
For child element creation
/// <summary>
/// creates and returns roommessage nood
/// </summary>
/// <param name="memberHesapAdi">the sender of the message</param>
/// <param name="message">sent message</param>
/// <param name="timeSent">the time when the message was sent</param>
/// <returns></returns>
private XElement CreateRoomIncomingMemberXElement(string memberHesapAdi, string entranceTime)
{
XElement roomMessage = new XElement("RoomMember",
new XElement("MemberHesapAdi", memberHesapAdi),
new XElement("Time", entranceTime));
return roomMessage;
}
In CreateRoomIncomingMemberXElement method you will create your own child element with your requirements, you will call it in AddMemberNodeToRoomMembersXMLWithGivenModelHesapAdiAndUyeHesapAdi and add it to the loaded file, then save it.
You can use the XDocument class to easily manipulate Xml in C#:
var doc = XDocument.Parse(yourXmlString); // Or XDocument.Load(pathToFile);
var childElement = new XElement("YourChildElementName", yourChildElementValue);
doc.Add(childElement);