+ ${appsetting:item=mysetting:default=mydefault} - produces "mydefault" if no appsetting
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ | Condition | +Foreground Color | +Background Color | +
|---|---|---|
| level == LogLevel.Fatal | +Red | +NoChange | +
| level == LogLevel.Error | +Yellow | +NoChange | +
| level == LogLevel.Warn | +Magenta | +NoChange | +
| level == LogLevel.Info | +White | +NoChange | +
| level == LogLevel.Debug | +Gray | +NoChange | +
| level == LogLevel.Trace | +DarkGray | +NoChange | +
+ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + Mail target works best when used with BufferingWrapper target + which lets you send multiple log messages in single mail +
++ To set up the buffered mail target in the configuration file, + use the following syntax: +
+
+ + To set up the buffered mail target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ ${appsetting:item=mysetting:default=mydefault} - produces "mydefault" if no appsetting
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : AsyncTaskTarget
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private async Task SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ return CustomWriteAsync(logEvent, token);
+ }
+
+ private async Task CustomWriteAsync(LogEventInfo logEvent, CancellationToken token)
+ {
+ await MyLogMethodAsync(logEvent, token).ConfigureAwait(false);
+ }
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ | Condition | +Foreground Color | +Background Color | +
|---|---|---|
| level == LogLevel.Fatal | +Red | +NoChange | +
| level == LogLevel.Error | +Yellow | +NoChange | +
| level == LogLevel.Warn | +Magenta | +NoChange | +
| level == LogLevel.Info | +White | +NoChange | +
| level == LogLevel.Debug | +Gray | +NoChange | +
| level == LogLevel.Trace | +DarkGray | +NoChange | +
+ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + Mail target works best when used with BufferingWrapper target + which lets you send multiple log messages in single mail +
++ To set up the buffered mail target in the configuration file, + use the following syntax: +
+
+ + To set up the buffered mail target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ ${appsetting:item=mysetting:default=mydefault} - produces "mydefault" if no appsetting
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : AsyncTaskTarget
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private async Task SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ return CustomWriteAsync(logEvent, token);
+ }
+
+ private async Task CustomWriteAsync(LogEventInfo logEvent, CancellationToken token)
+ {
+ await MyLogMethodAsync(logEvent, token).ConfigureAwait(false);
+ }
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ | Condition | +Foreground Color | +Background Color | +
|---|---|---|
| level == LogLevel.Fatal | +Red | +NoChange | +
| level == LogLevel.Error | +Yellow | +NoChange | +
| level == LogLevel.Warn | +Magenta | +NoChange | +
| level == LogLevel.Info | +White | +NoChange | +
| level == LogLevel.Debug | +Gray | +NoChange | +
| level == LogLevel.Trace | +DarkGray | +NoChange | +
+ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + Mail target works best when used with BufferingWrapper target + which lets you send multiple log messages in single mail +
++ To set up the buffered mail target in the configuration file, + use the following syntax: +
+
+ + To set up the buffered mail target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : AsyncTaskTarget
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private async Task SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ return CustomWriteAsync(logEvent, token);
+ }
+
+ private async Task CustomWriteAsync(LogEventInfo logEvent, CancellationToken token)
+ {
+ await MyLogMethodAsync(logEvent, token).ConfigureAwait(false);
+ }
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : AsyncTaskTarget
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private async Task SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ return CustomWriteAsync(logEvent, token);
+ }
+
+ private async Task CustomWriteAsync(LogEventInfo logEvent, CancellationToken token)
+ {
+ await MyLogMethodAsync(logEvent, token).ConfigureAwait(false);
+ }
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ | Condition | +Foreground Color | +Background Color | +
|---|---|---|
| level == LogLevel.Fatal | +Red | +NoChange | +
| level == LogLevel.Error | +Yellow | +NoChange | +
| level == LogLevel.Warn | +Magenta | +NoChange | +
| level == LogLevel.Info | +White | +NoChange | +
| level == LogLevel.Debug | +Gray | +NoChange | +
| level == LogLevel.Trace | +DarkGray | +NoChange | +
+ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : AsyncTaskTarget
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private async Task SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+
+ protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
+ {
+ return CustomWriteAsync(logEvent, token);
+ }
+
+ private async Task CustomWriteAsync(LogEventInfo logEvent, CancellationToken token)
+ {
+ await MyLogMethodAsync(logEvent, token).ConfigureAwait(false);
+ }
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ | Condition | +Foreground Color | +Background Color | +
|---|---|---|
| level == LogLevel.Fatal | +Red | +NoChange | +
| level == LogLevel.Error | +Yellow | +NoChange | +
| level == LogLevel.Warn | +Magenta | +NoChange | +
| level == LogLevel.Info | +White | +NoChange | +
| level == LogLevel.Debug | +Gray | +NoChange | +
| level == LogLevel.Trace | +DarkGray | +NoChange | +
+ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${basedir}/${level}.log
+ All + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + Mail target works best when used with BufferingWrapper target + which lets you send multiple log messages in single mail +
++ To set up the buffered mail target in the configuration file, + use the following syntax: +
+
+ + To set up the buffered mail target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To print the results, use any application that's able to receive messages over + TCP or UDP. NetCat is + a simple but very powerful command-line tool that can be used for that. This image + demonstrates the NetCat tool receiving log messages from Network target. +
+
+ + There are two specialized versions of the Network target: Chainsaw + and NLogViewer which write to instances of Chainsaw log4j viewer + or NLogViewer application respectively. +
+${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [Target("MyFirst")]
+ public sealed class MyFirstTarget : TargetWithContext
+ {
+ public MyFirstTarget()
+ {
+ this.Host = "localhost";
+ }
+
+ [RequiredParameter]
+ public Layout Host { get; set; }
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ string logMessage = this.RenderLogEvent(this.Layout, logEvent);
+ string hostName = this.RenderLogEvent(this.Host, logEvent);
+ return SendTheMessageToRemoteHost(hostName, logMessage);
+ }
+
+ private void SendTheMessageToRemoteHost(string hostName, string message)
+ {
+ // To be implemented
+ }
+ }
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+ ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}
+
+ <system.net>
+ <defaultProxy enabled = "true" useDefaultCredentials = "true" >
+ <proxy usesystemdefault = "True" />
+ </defaultProxy>
+ </system.net>
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ The example web service that works with this example is shown below
+
+ + Asynchronous target wrapper allows the logger code to execute more quickly, by queuing + messages and processing them in a separate thread. You should wrap targets + that spend a non-trivial amount of time in their Write() method with asynchronous + target to speed up logging. +
++ Because asynchronous logging is quite a common scenario, NLog supports a + shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to + the <targets/> element in the configuration file. +
+
+
+ ... your targets go here ...
+
+ ]]>
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to server1, + and if it fails, messages go to server2.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages not contains the string '1' to be ignored.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ + This example works like this. If there are no Warn,Error or Fatal messages in the buffer + only Info messages are written to the file, but if there are any warnings or errors, + the output includes detailed trace (levels >= Debug). You can plug in a different type + of buffering wrapper (such as ASPNetBufferingWrapper) to achieve different + functionality. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt + chosen randomly on a per-message basis. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each log message to be repeated 3 times.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes each write attempt to be repeated 3 times, + sleeping 1 second between attempts if first one fails.
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to either file1.txt or file2.txt. + Each odd message is written to file2.txt, each even message goes to file1.txt. +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+ This example causes the messages to be written to both file1.txt or file2.txt +
++ To set up the target in the configuration file, + use the following syntax: +
+
+ + To set up the log target programmatically use code like this: +
+
+
+ [CanBeNull] object Test() => null;
+
+ void UseTest() {
+ var p = Test();
+ var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+ }
+
+ [NotNull] object Foo() {
+ return null; // Warning: Possible 'null' assignment
+ }
+
+ public void Foo([ItemNotNull]List<string> books)
+ {
+ foreach (var book in books) {
+ if (book != null) // Warning: Expression is always true
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ public void Foo([ItemCanBeNull]List<string> books)
+ {
+ foreach (var book in books)
+ {
+ // Warning: Possible 'System.NullReferenceException'
+ Console.WriteLine(book.ToUpper());
+ }
+ }
+
+ [StringFormatMethod("message")]
+ void ShowError(string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+ }
+
+ void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ }
+
+ void Foo() {
+ LogInfo("User created: {username}"); // Warning: Non-existing argument in format string
+ }
+
+ namespace TestNamespace
+ {
+ public class Constants
+ {
+ public static int INT_CONST = 1;
+ public const string STRING_CONST = "1";
+ }
+
+ public class Class1
+ {
+ [ValueProvider("TestNamespace.Constants")] public int myField;
+ public void Foo([ValueProvider("TestNamespace.Constants")] string str) { }
+
+ public void Test()
+ {
+ Foo(/*try completion here*/);//
+ myField = /*try completion here*/
+ }
+ }
+ }
+
+ void Foo([ValueRange(0, 100)] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo([NonNegativeValue] int value) {
+ if (value == -1) { // Warning: Expression is always 'false'
+ ...
+ }
+ }
+
+ void Foo(string param) {
+ if (param == null)
+ throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+ }
+
+ public class Foo : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void NotifyChanged(string propertyName) { ... }
+
+ string _name;
+
+ public string Name {
+ get { return _name; }
+ set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+ }
+ }
+
+ Examples of generated notifications:
+ Function Definition Table syntax:
+
+ [ContractAnnotation("=> halt")]
+ public void TerminationMethod()
+
+ [ContractAnnotation("null <= param:null")] // reverse condition syntax
+ public string GetName(string surname)
+
+ [ContractAnnotation("s:null => true")]
+ public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+
+ // A method that returns null if the parameter is null,
+ // and not null if the parameter is not null
+ [ContractAnnotation("null => null; notnull => notnull")]
+ public object Transform(object data)
+
+ [ContractAnnotation("=> true, result: notnull; => false, result: null")]
+ public bool TryParse(string s, out Person result)
+
+ [LocalizationRequiredAttribute(true)]
+ class Foo {
+ string str = "my string"; // Warning: Localizable string
+ }
+
+ [CannotApplyEqualityOperator]
+ class NoEquality { }
+
+ class UsesNoEquality {
+ void Test() {
+ var ca1 = new NoEquality();
+ var ca2 = new NoEquality();
+ if (ca1 != null) { // OK
+ bool condition = ca1 == ca2; // Warning
+ }
+ }
+ }
+
+ [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+ class ComponentAttribute : Attribute { }
+
+ [Component] // ComponentAttribute requires implementing IComponent interface
+ class MyComponent : IComponent { }
+
+ [UsedImplicitly]
+ public class TypeConverter {}
+
+ public class SummaryData
+ {
+ [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
+ public SummaryData() {}
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)]
+ public interface IService {}
+
+ [Pure] int Multiply(int x, int y) => x * y;
+
+ void M() {
+ Multiply(123, 42); // Warning: Return value of pure method is not used
+ }
+ [MustUseReturnValue("Use the return value to...")].
+
+ class Foo {
+ [ProvidesContext] IBarService _barService = ...;
+
+ void ProcessNode(INode node) {
+ DoSomething(node, node.GetGlobalServices().Bar);
+ // ^ Warning: use value of '_barService' field
+ }
+ }
+
+ [SourceTemplate]
+ public static void forEach<T>(this IEnumerable<T> xs) {
+ foreach (var x in xs) {
+ //$ $END$
+ }
+ }
+
+
+ [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
+ public static void forEach<T>(this IEnumerable<T> collection) {
+ foreach (var item in collection) {
+ //$ $END$
+ }
+ }
+
+ Applying the attribute on a template method parameter:
+
+ [SourceTemplate]
+ public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
+ /*$ var $x$Id = "$newguid$" + x.ToString();
+ x.DoSomething($x$Id); */
+ }
+
+
+ public class MyStringCollection : List<string>
+ {
+ [CollectionAccess(CollectionAccessType.Read)]
+ public string GetFirstString()
+ {
+ return this.ElementAt(0);
+ }
+ }
+ class Test
+ {
+ public void Foo()
+ {
+ // Warning: Contents of the collection is never updated
+ var col = new MyStringCollection();
+ string x = col.GetFirstString();
+ }
+ }
+
+ static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class
+ {
+ // custom check for null but no enumeration
+ }
+
+ void Foo(IEnumerable<string> values)
+ {
+ ThrowIfNull(values, nameof(values));
+ var x = values.ToList(); // No warnings about multiple enumeration
+ }
+
+ void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps)
+ {
+ // cssProps should only contains a list of CSS properties
+ }
+
+ WARNING: This method is not officially supported for external callers and
+ should be considered "experimental", even though it is "public".
+
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Attempts to decrypt a database file that was encrypted using the
+ legacy CryptoAPI-based (RC4) codec that was previously included with
+ System.Data.SQLite.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ int (*xCreate)(sqlite3 *db, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+
+ int sqlite3_vtab_config(sqlite3 *db, int op, ...);
+
+ CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+
+ CREATE TABLE generate_series(
+ value,
+ start HIDDEN,
+ stop HIDDEN,
+ step HIDDEN
+ );
+
+ SELECT value FROM generate_series(5,50);
+
+ SELECT value FROM generate_series WHERE start=5 AND stop=50;
+
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+
+ struct sqlite3_index_info {
+ /* Inputs */
+ const int nConstraint; /* Number of entries in aConstraint */
+ const struct sqlite3_index_constraint {
+ int iColumn; /* Column constrained. -1 for ROWID */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *const aConstraint; /* Table of WHERE clause constraints */
+ const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ const struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *const aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *const aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+ ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ };
+
+ #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ #define SQLITE_INDEX_CONSTRAINT_GT 4
+ #define SQLITE_INDEX_CONSTRAINT_LE 8
+ #define SQLITE_INDEX_CONSTRAINT_LT 16
+ #define SQLITE_INDEX_CONSTRAINT_GE 32
+ #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_LIMIT 73 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_OFFSET 74 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+ const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);
+
+ column OP EXPR
+
+ a = 5
+
+ x BETWEEN 10 AND 100 AND 999>y
+
+ x >= 10
+ x <= 100
+ y < 999
+
+ FUNCTION( column, EXPR)
+
+ LIKE(EXPR, column)]]>
+ GLOB(EXPR, column)]]>
+ REGEXP(EXPR, column)]]>
+ MATCH(EXPR, column)]]>
+
+ column LIKE EXPR]]>
+ LIKE(EXPR,column)
+
+ SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+
+ SELECT * FROM realtab, tablevaluedfunc
+ WHERE tablevaluedfunc.param1 = realtab.x;
+
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+
+ int (*xClose)(sqlite3_vtab_cursor*);
+
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+
+ int (*xNext)(sqlite3_vtab_cursor*);
+
+ int (*xEof)(sqlite3_vtab_cursor*);
+
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+
+ int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+
+ int (*xUpdate)(
+ sqlite3_vtab *pVTab,
+ int argc,
+ sqlite3_value **argv,
+ sqlite_int64 *pRowid
+ );
+
+ ]]>
+ ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ ]]>]]>
+ DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ No insert occurs.
+ ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ ]]>]]>
+ INSERT: A new row is inserted with column values taken from
+ argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ then a new unique rowid is generated automatically. The argv[1] will be NULL
+ for a WITHOUT ROWID virtual table, in which case the implementation should
+ take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ ]]>]]>
+ UPDATE:
+ The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ in argv[2] and following parameters.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ ]]>]]>
+ UPDATE with rowid or PRIMARY KEY change:
+ The row with rowid or PRIMARY KEY argv[0] is updated with
+ the rowid or PRIMARY KEY in argv[1]
+ and new values in argv[2] and following parameters. This will occur
+ when an SQL statement updates a rowid, as in the statement:
+
+ UPDATE table SET rowid=rowid+1 WHERE ...;
+
+ ]]>]]>
+
+ int (*xBegin)(sqlite3_vtab *pVTab);
+
+ int (*xSync)(sqlite3_vtab *pVTab);
+
+ int (*xCommit)(sqlite3_vtab *pVTab);
+
+ int (*xRollback)(sqlite3_vtab *pVTab);
+
+ int (*xFindFunction)(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+ );
+
+ SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);
+
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+
+ public static class Sample
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=:memory:;"))
+ {
+ connection.Open();
+
+ connection.CreateModule(new SQLiteModuleEnumerable(
+ "sampleModule", new string[] { "one", "two", "three" }));
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText =
+ "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "SELECT * FROM t1;";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ while (dataReader.Read())
+ Console.WriteLine(dataReader[0].ToString());
+ }
+ }
+
+ connection.Close();
+ }
+ }
+ }
+
+
+ WARNING: This method is not officially supported for external callers and
+ should be considered "experimental", even though it is "public".
+
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Attempts to decrypt a database file that was encrypted using the
+ legacy CryptoAPI-based (RC4) codec that was previously included with
+ System.Data.SQLite.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ int (*xCreate)(sqlite3 *db, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+
+ int sqlite3_vtab_config(sqlite3 *db, int op, ...);
+
+ CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+
+ CREATE TABLE generate_series(
+ value,
+ start HIDDEN,
+ stop HIDDEN,
+ step HIDDEN
+ );
+
+ SELECT value FROM generate_series(5,50);
+
+ SELECT value FROM generate_series WHERE start=5 AND stop=50;
+
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+
+ struct sqlite3_index_info {
+ /* Inputs */
+ const int nConstraint; /* Number of entries in aConstraint */
+ const struct sqlite3_index_constraint {
+ int iColumn; /* Column constrained. -1 for ROWID */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *const aConstraint; /* Table of WHERE clause constraints */
+ const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ const struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *const aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *const aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+ ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ };
+
+ #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ #define SQLITE_INDEX_CONSTRAINT_GT 4
+ #define SQLITE_INDEX_CONSTRAINT_LE 8
+ #define SQLITE_INDEX_CONSTRAINT_LT 16
+ #define SQLITE_INDEX_CONSTRAINT_GE 32
+ #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_LIMIT 73 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_OFFSET 74 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+ const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);
+
+ column OP EXPR
+
+ a = 5
+
+ x BETWEEN 10 AND 100 AND 999>y
+
+ x >= 10
+ x <= 100
+ y < 999
+
+ FUNCTION( column, EXPR)
+
+ LIKE(EXPR, column)]]>
+ GLOB(EXPR, column)]]>
+ REGEXP(EXPR, column)]]>
+ MATCH(EXPR, column)]]>
+
+ column LIKE EXPR]]>
+ LIKE(EXPR,column)
+
+ SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+
+ SELECT * FROM realtab, tablevaluedfunc
+ WHERE tablevaluedfunc.param1 = realtab.x;
+
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+
+ int (*xClose)(sqlite3_vtab_cursor*);
+
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+
+ int (*xNext)(sqlite3_vtab_cursor*);
+
+ int (*xEof)(sqlite3_vtab_cursor*);
+
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+
+ int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+
+ int (*xUpdate)(
+ sqlite3_vtab *pVTab,
+ int argc,
+ sqlite3_value **argv,
+ sqlite_int64 *pRowid
+ );
+
+ ]]>
+ ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ ]]>]]>
+ DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ No insert occurs.
+ ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ ]]>]]>
+ INSERT: A new row is inserted with column values taken from
+ argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ then a new unique rowid is generated automatically. The argv[1] will be NULL
+ for a WITHOUT ROWID virtual table, in which case the implementation should
+ take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ ]]>]]>
+ UPDATE:
+ The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ in argv[2] and following parameters.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ ]]>]]>
+ UPDATE with rowid or PRIMARY KEY change:
+ The row with rowid or PRIMARY KEY argv[0] is updated with
+ the rowid or PRIMARY KEY in argv[1]
+ and new values in argv[2] and following parameters. This will occur
+ when an SQL statement updates a rowid, as in the statement:
+
+ UPDATE table SET rowid=rowid+1 WHERE ...;
+
+ ]]>]]>
+
+ int (*xBegin)(sqlite3_vtab *pVTab);
+
+ int (*xSync)(sqlite3_vtab *pVTab);
+
+ int (*xCommit)(sqlite3_vtab *pVTab);
+
+ int (*xRollback)(sqlite3_vtab *pVTab);
+
+ int (*xFindFunction)(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+ );
+
+ SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);
+
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+
+ public static class Sample
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=:memory:;"))
+ {
+ connection.Open();
+
+ connection.CreateModule(new SQLiteModuleEnumerable(
+ "sampleModule", new string[] { "one", "two", "three" }));
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText =
+ "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "SELECT * FROM t1;";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ while (dataReader.Read())
+ Console.WriteLine(dataReader[0].ToString());
+ }
+ }
+
+ connection.Close();
+ }
+ }
+ }
+
+
+ WARNING: This method is not officially supported for external callers and
+ should be considered "experimental", even though it is "public".
+
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Attempts to decrypt a database file that was encrypted using the
+ legacy CryptoAPI-based (RC4) codec that was previously included with
+ System.Data.SQLite.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ int (*xCreate)(sqlite3 *db, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+
+ int sqlite3_vtab_config(sqlite3 *db, int op, ...);
+
+ CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+
+ CREATE TABLE generate_series(
+ value,
+ start HIDDEN,
+ stop HIDDEN,
+ step HIDDEN
+ );
+
+ SELECT value FROM generate_series(5,50);
+
+ SELECT value FROM generate_series WHERE start=5 AND stop=50;
+
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+
+ struct sqlite3_index_info {
+ /* Inputs */
+ const int nConstraint; /* Number of entries in aConstraint */
+ const struct sqlite3_index_constraint {
+ int iColumn; /* Column constrained. -1 for ROWID */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *const aConstraint; /* Table of WHERE clause constraints */
+ const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ const struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *const aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *const aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+ ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ };
+
+ #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ #define SQLITE_INDEX_CONSTRAINT_GT 4
+ #define SQLITE_INDEX_CONSTRAINT_LE 8
+ #define SQLITE_INDEX_CONSTRAINT_LT 16
+ #define SQLITE_INDEX_CONSTRAINT_GE 32
+ #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_LIMIT 73 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_OFFSET 74 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+ const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);
+
+ column OP EXPR
+
+ a = 5
+
+ x BETWEEN 10 AND 100 AND 999>y
+
+ x >= 10
+ x <= 100
+ y < 999
+
+ FUNCTION( column, EXPR)
+
+ LIKE(EXPR, column)]]>
+ GLOB(EXPR, column)]]>
+ REGEXP(EXPR, column)]]>
+ MATCH(EXPR, column)]]>
+
+ column LIKE EXPR]]>
+ LIKE(EXPR,column)
+
+ SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+
+ SELECT * FROM realtab, tablevaluedfunc
+ WHERE tablevaluedfunc.param1 = realtab.x;
+
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+
+ int (*xClose)(sqlite3_vtab_cursor*);
+
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+
+ int (*xNext)(sqlite3_vtab_cursor*);
+
+ int (*xEof)(sqlite3_vtab_cursor*);
+
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+
+ int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+
+ int (*xUpdate)(
+ sqlite3_vtab *pVTab,
+ int argc,
+ sqlite3_value **argv,
+ sqlite_int64 *pRowid
+ );
+
+ ]]>
+ ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ ]]>]]>
+ DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ No insert occurs.
+ ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ ]]>]]>
+ INSERT: A new row is inserted with column values taken from
+ argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ then a new unique rowid is generated automatically. The argv[1] will be NULL
+ for a WITHOUT ROWID virtual table, in which case the implementation should
+ take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ ]]>]]>
+ UPDATE:
+ The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ in argv[2] and following parameters.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ ]]>]]>
+ UPDATE with rowid or PRIMARY KEY change:
+ The row with rowid or PRIMARY KEY argv[0] is updated with
+ the rowid or PRIMARY KEY in argv[1]
+ and new values in argv[2] and following parameters. This will occur
+ when an SQL statement updates a rowid, as in the statement:
+
+ UPDATE table SET rowid=rowid+1 WHERE ...;
+
+ ]]>]]>
+
+ int (*xBegin)(sqlite3_vtab *pVTab);
+
+ int (*xSync)(sqlite3_vtab *pVTab);
+
+ int (*xCommit)(sqlite3_vtab *pVTab);
+
+ int (*xRollback)(sqlite3_vtab *pVTab);
+
+ int (*xFindFunction)(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+ );
+
+ SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);
+
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+
+ public static class Sample
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=:memory:;"))
+ {
+ connection.Open();
+
+ connection.CreateModule(new SQLiteModuleEnumerable(
+ "sampleModule", new string[] { "one", "two", "three" }));
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText =
+ "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "SELECT * FROM t1;";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ while (dataReader.Read())
+ Console.WriteLine(dataReader[0].ToString());
+ }
+ }
+
+ connection.Close();
+ }
+ }
+ }
+
+
+ WARNING: This method is not officially supported for external callers and
+ should be considered "experimental", even though it is "public".
+
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Attempts to decrypt a database file that was encrypted using the
+ legacy CryptoAPI-based (RC4) codec that was previously included with
+ System.Data.SQLite.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ int (*xCreate)(sqlite3 *db, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+
+ int sqlite3_vtab_config(sqlite3 *db, int op, ...);
+
+ CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+
+ CREATE TABLE generate_series(
+ value,
+ start HIDDEN,
+ stop HIDDEN,
+ step HIDDEN
+ );
+
+ SELECT value FROM generate_series(5,50);
+
+ SELECT value FROM generate_series WHERE start=5 AND stop=50;
+
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+
+ struct sqlite3_index_info {
+ /* Inputs */
+ const int nConstraint; /* Number of entries in aConstraint */
+ const struct sqlite3_index_constraint {
+ int iColumn; /* Column constrained. -1 for ROWID */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *const aConstraint; /* Table of WHERE clause constraints */
+ const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ const struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *const aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *const aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+ ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ };
+
+ #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ #define SQLITE_INDEX_CONSTRAINT_GT 4
+ #define SQLITE_INDEX_CONSTRAINT_LE 8
+ #define SQLITE_INDEX_CONSTRAINT_LT 16
+ #define SQLITE_INDEX_CONSTRAINT_GE 32
+ #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_LIMIT 73 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_OFFSET 74 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+ const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);
+
+ column OP EXPR
+
+ a = 5
+
+ x BETWEEN 10 AND 100 AND 999>y
+
+ x >= 10
+ x <= 100
+ y < 999
+
+ FUNCTION( column, EXPR)
+
+ LIKE(EXPR, column)]]>
+ GLOB(EXPR, column)]]>
+ REGEXP(EXPR, column)]]>
+ MATCH(EXPR, column)]]>
+
+ column LIKE EXPR]]>
+ LIKE(EXPR,column)
+
+ SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+
+ SELECT * FROM realtab, tablevaluedfunc
+ WHERE tablevaluedfunc.param1 = realtab.x;
+
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+
+ int (*xClose)(sqlite3_vtab_cursor*);
+
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+
+ int (*xNext)(sqlite3_vtab_cursor*);
+
+ int (*xEof)(sqlite3_vtab_cursor*);
+
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+
+ int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+
+ int (*xUpdate)(
+ sqlite3_vtab *pVTab,
+ int argc,
+ sqlite3_value **argv,
+ sqlite_int64 *pRowid
+ );
+
+ ]]>
+ ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ ]]>]]>
+ DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ No insert occurs.
+ ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ ]]>]]>
+ INSERT: A new row is inserted with column values taken from
+ argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ then a new unique rowid is generated automatically. The argv[1] will be NULL
+ for a WITHOUT ROWID virtual table, in which case the implementation should
+ take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ ]]>]]>
+ UPDATE:
+ The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ in argv[2] and following parameters.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ ]]>]]>
+ UPDATE with rowid or PRIMARY KEY change:
+ The row with rowid or PRIMARY KEY argv[0] is updated with
+ the rowid or PRIMARY KEY in argv[1]
+ and new values in argv[2] and following parameters. This will occur
+ when an SQL statement updates a rowid, as in the statement:
+
+ UPDATE table SET rowid=rowid+1 WHERE ...;
+
+ ]]>]]>
+
+ int (*xBegin)(sqlite3_vtab *pVTab);
+
+ int (*xSync)(sqlite3_vtab *pVTab);
+
+ int (*xCommit)(sqlite3_vtab *pVTab);
+
+ int (*xRollback)(sqlite3_vtab *pVTab);
+
+ int (*xFindFunction)(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+ );
+
+ SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);
+
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+
+ public static class Sample
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=:memory:;"))
+ {
+ connection.Open();
+
+ connection.CreateModule(new SQLiteModuleEnumerable(
+ "sampleModule", new string[] { "one", "two", "three" }));
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText =
+ "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "SELECT * FROM t1;";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ while (dataReader.Read())
+ Console.WriteLine(dataReader[0].ToString());
+ }
+ }
+
+ connection.Close();
+ }
+ }
+ }
+
+
+ WARNING: This method is not officially supported for external callers and
+ should be considered "experimental", even though it is "public".
+
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Attempts to decrypt a database file that was encrypted using the
+ legacy CryptoAPI-based (RC4) codec that was previously included with
+ System.Data.SQLite.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Change the password (or assign a password) to the open database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ WARNING: This method always throws an exception unless the library
+ has been built with encryption support.
+
+ Sets existing password for a closed password-protected database.
+ Password-protected databases are unusable for any other operation
+ until the password has been set. Generally, this is accomplished
+ via setting one of the "*Password" connection string properties,
+ because it must be done prior to opening the database.
+
+ int (*xCreate)(sqlite3 *db, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+
+ int sqlite3_vtab_config(sqlite3 *db, int op, ...);
+
+ CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+
+ CREATE TABLE generate_series(
+ value,
+ start HIDDEN,
+ stop HIDDEN,
+ step HIDDEN
+ );
+
+ SELECT value FROM generate_series(5,50);
+
+ SELECT value FROM generate_series WHERE start=5 AND stop=50;
+
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr);
+
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+
+ struct sqlite3_index_info {
+ /* Inputs */
+ const int nConstraint; /* Number of entries in aConstraint */
+ const struct sqlite3_index_constraint {
+ int iColumn; /* Column constrained. -1 for ROWID */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *const aConstraint; /* Table of WHERE clause constraints */
+ const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ const struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *const aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *const aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+ ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ };
+
+ #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ #define SQLITE_INDEX_CONSTRAINT_GT 4
+ #define SQLITE_INDEX_CONSTRAINT_LE 8
+ #define SQLITE_INDEX_CONSTRAINT_LT 16
+ #define SQLITE_INDEX_CONSTRAINT_GE 32
+ #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_LIMIT 73 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_OFFSET 74 /* 3.38.0 and later */
+ #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+ const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);
+
+ column OP EXPR
+
+ a = 5
+
+ x BETWEEN 10 AND 100 AND 999>y
+
+ x >= 10
+ x <= 100
+ y < 999
+
+ FUNCTION( column, EXPR)
+
+ LIKE(EXPR, column)]]>
+ GLOB(EXPR, column)]]>
+ REGEXP(EXPR, column)]]>
+ MATCH(EXPR, column)]]>
+
+ column LIKE EXPR]]>
+ LIKE(EXPR,column)
+
+ SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+
+ SELECT * FROM realtab, tablevaluedfunc
+ WHERE tablevaluedfunc.param1 = realtab.x;
+
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+
+ int (*xClose)(sqlite3_vtab_cursor*);
+
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+
+ int (*xNext)(sqlite3_vtab_cursor*);
+
+ int (*xEof)(sqlite3_vtab_cursor*);
+
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+
+ int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+
+ int (*xUpdate)(
+ sqlite3_vtab *pVTab,
+ int argc,
+ sqlite3_value **argv,
+ sqlite_int64 *pRowid
+ );
+
+ ]]>
+ ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ ]]>]]>
+ DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ No insert occurs.
+ ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ ]]>]]>
+ INSERT: A new row is inserted with column values taken from
+ argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ then a new unique rowid is generated automatically. The argv[1] will be NULL
+ for a WITHOUT ROWID virtual table, in which case the implementation should
+ take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ ]]>]]>
+ UPDATE:
+ The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ in argv[2] and following parameters.
+ ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ ]]>]]>
+ UPDATE with rowid or PRIMARY KEY change:
+ The row with rowid or PRIMARY KEY argv[0] is updated with
+ the rowid or PRIMARY KEY in argv[1]
+ and new values in argv[2] and following parameters. This will occur
+ when an SQL statement updates a rowid, as in the statement:
+
+ UPDATE table SET rowid=rowid+1 WHERE ...;
+
+ ]]>]]>
+
+ int (*xBegin)(sqlite3_vtab *pVTab);
+
+ int (*xSync)(sqlite3_vtab *pVTab);
+
+ int (*xCommit)(sqlite3_vtab *pVTab);
+
+ int (*xRollback)(sqlite3_vtab *pVTab);
+
+ int (*xFindFunction)(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+ );
+
+ SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);
+
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ int (*xRelease)(sqlite3_vtab *pVtab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+
+ (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+
+ public static class Sample
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=:memory:;"))
+ {
+ connection.Open();
+
+ connection.CreateModule(new SQLiteModuleEnumerable(
+ "sampleModule", new string[] { "one", "two", "three" }));
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText =
+ "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "SELECT * FROM t1;";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ while (dataReader.Read())
+ Console.WriteLine(dataReader[0].ToString());
+ }
+ }
+
+ connection.Close();
+ }
+ }
+ }
+
+