Working on initial project structure.
This commit is contained in:
parent
a3d6749271
commit
41cd87b2f4
263
.gitignore
vendored
Normal file
263
.gitignore
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
#Mac Store files
|
||||
.DS_Store
|
BIN
Logo_Symbol_Black_Outline.png
Normal file
BIN
Logo_Symbol_Black_Outline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
16
MySqlPlus.Example/MySqlPlus.Example.csproj
Normal file
16
MySqlPlus.Example/MySqlPlus.Example.csproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AssemblyName>MontoyaTech.MySqlPlus.Example</AssemblyName>
|
||||
<RootNamespace>MontoyaTech.MySqlPlus.Example</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MySqlPlus\MySqlPlus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
34
MySqlPlus.Example/Program.cs
Normal file
34
MySqlPlus.Example/Program.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus.Example
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public class Car : MySqlRow
|
||||
{
|
||||
[MySqlRowId]
|
||||
[MySqlColumn]
|
||||
public ulong Id = 0;
|
||||
|
||||
[MySqlColumn("make")]
|
||||
[MySqlColumnAlias("Make")]
|
||||
public string Make = null;
|
||||
|
||||
[MySqlColumn("mode")]
|
||||
public string Model = null;
|
||||
|
||||
[MySqlColumn("dateCreated", typeof(DateTime2UnixConverter))]
|
||||
public DateTime DateCreated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class DateTime2UnixConverter
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello, World!");
|
||||
}
|
||||
}
|
||||
}
|
30
MySqlPlus.Tests/MySqlPlus.Tests.csproj
Normal file
30
MySqlPlus.Tests/MySqlPlus.Tests.csproj
Normal file
@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AssemblyName>MontoyaTech.MySqlPlus.Tests</AssemblyName>
|
||||
<RootNamespace>MontoyaTech.MySqlPlus.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MySqlPlus\MySqlPlus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
37
MySqlPlus.sln
Normal file
37
MySqlPlus.sln
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33213.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlPlus", "MySqlPlus\MySqlPlus.csproj", "{EC8F451D-E711-4FF2-87F5-B65F21570785}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlPlus.Example", "MySqlPlus.Example\MySqlPlus.Example.csproj", "{2DC4B2B2-D391-475C-A792-D451A765259E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlPlus.Tests", "MySqlPlus.Tests\MySqlPlus.Tests.csproj", "{D10C9285-D11A-43FC-A234-1D5846CD97F8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EC8F451D-E711-4FF2-87F5-B65F21570785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EC8F451D-E711-4FF2-87F5-B65F21570785}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EC8F451D-E711-4FF2-87F5-B65F21570785}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EC8F451D-E711-4FF2-87F5-B65F21570785}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2DC4B2B2-D391-475C-A792-D451A765259E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2DC4B2B2-D391-475C-A792-D451A765259E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2DC4B2B2-D391-475C-A792-D451A765259E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2DC4B2B2-D391-475C-A792-D451A765259E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D10C9285-D11A-43FC-A234-1D5846CD97F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D10C9285-D11A-43FC-A234-1D5846CD97F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D10C9285-D11A-43FC-A234-1D5846CD97F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D10C9285-D11A-43FC-A234-1D5846CD97F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AC9C73B2-A516-4430-9434-CB2AF7B47147}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
165
MySqlPlus/GlobalTimeStamp.cs
Normal file
165
MySqlPlus/GlobalTimeStamp.cs
Normal file
@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
/// <summary>
|
||||
/// This timestamp runs on its own and it
|
||||
/// uses very little CPU usage to keep track of time. This is a very
|
||||
/// useful system. This timestamp is in elapsed miliseconds.
|
||||
/// </summary>
|
||||
internal class GlobalTimeStamp : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the instance of the time stamp which is used
|
||||
/// to auto start this.
|
||||
/// </summary>
|
||||
public static GlobalTimeStamp instance = new GlobalTimeStamp();
|
||||
|
||||
/// <summary>
|
||||
/// The Current Timestamp, elapsed time since the begining in Miliseconds.
|
||||
/// </summary>
|
||||
public static ulong Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance._Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The StopWatch that the TimeStamp uses
|
||||
/// </summary>
|
||||
private Stopwatch Watch = null;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this Timestamp Is Running.
|
||||
/// </summary>
|
||||
private bool Running = false;
|
||||
|
||||
/// <summary>
|
||||
/// The Refresh Thread for this GlobalTimeStamp.
|
||||
/// </summary>
|
||||
private Thread RefreshThread = null;
|
||||
|
||||
/// <summary>
|
||||
/// The Current Timestamp, elapsed time since the begining in Miliseconds.
|
||||
/// </summary>
|
||||
public ulong _Current = 777; //Initialize this value so we have something.
|
||||
|
||||
/// <summary>
|
||||
/// A new instance of the GlobalTimeStamp
|
||||
/// </summary>
|
||||
public GlobalTimeStamp()
|
||||
{
|
||||
//Start the watch.
|
||||
this.Watch = new Stopwatch();
|
||||
this.Watch.Start();
|
||||
|
||||
//Setup the Refresh Thread
|
||||
this.Running = true;
|
||||
this.RefreshThread = new Thread(Refresh);
|
||||
this.RefreshThread.IsBackground = true;
|
||||
this.RefreshThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method that handles Refreshing the TimeStamp.
|
||||
/// </summary>
|
||||
private void Refresh()
|
||||
{
|
||||
while (this.Running)
|
||||
{
|
||||
//Get the elapsed miliseconds from the watch
|
||||
this._Current = (ulong)this.Watch.ElapsedMilliseconds;
|
||||
|
||||
//Now sleep 1MS
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Difference between two TimeStamps in miliseconds.
|
||||
/// Note:
|
||||
/// We needed this function because sometimes we
|
||||
/// store a timestamp of when an event occured but when
|
||||
/// using multi threading if this is greater than the last
|
||||
/// time we got the current time, then we need to select the bigger one.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns>The difference in miliseconds.</returns>
|
||||
public static ulong GetDifference(ulong a, ulong b)
|
||||
{
|
||||
if (a > b)
|
||||
return (a - b);
|
||||
|
||||
return (b - a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Difference in miliseconds between the Current Time and the passed
|
||||
/// Time stamp.
|
||||
/// Note:
|
||||
/// We needed this function because sometimes we
|
||||
/// store a timestamp of when an event occured but when
|
||||
/// using multi threading if this is greater than the last
|
||||
/// time we got the current time, then we need to select the bigger one.
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns>The difference in miliseconds.</returns>
|
||||
public static ulong GetDifference(ulong b)
|
||||
{
|
||||
//Get the current timestamp
|
||||
ulong current = instance._Current;
|
||||
//Compare it.
|
||||
if (current > b)
|
||||
return (current - b);
|
||||
|
||||
return (b - current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this GlobalTimeStamp if its Running.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Running)
|
||||
{
|
||||
this.Running = false;
|
||||
|
||||
this.Watch.Stop();
|
||||
|
||||
this.RefreshThread = null;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this GlobalTimeStamp is going to be destroyed.
|
||||
/// </summary>
|
||||
~GlobalTimeStamp()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Running)
|
||||
{
|
||||
this.Running = false;
|
||||
|
||||
this.Watch.Stop();
|
||||
|
||||
this.RefreshThread = null;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
22
MySqlPlus/MySqlColumn.cs
Normal file
22
MySqlPlus/MySqlColumn.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
public class MySqlColumn : Attribute
|
||||
{
|
||||
public string Name = null;
|
||||
|
||||
public Type Converter = null;
|
||||
|
||||
public MySqlColumn(string name = null, Type converter = null)
|
||||
{
|
||||
this.Name = name;
|
||||
|
||||
this.Converter = converter;
|
||||
}
|
||||
}
|
||||
}
|
18
MySqlPlus/MySqlColumnAlias.cs
Normal file
18
MySqlPlus/MySqlColumnAlias.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
public class MySqlColumnAlias : Attribute
|
||||
{
|
||||
public string Alias = null;
|
||||
|
||||
public MySqlColumnAlias(string alias)
|
||||
{
|
||||
this.Alias = alias;
|
||||
}
|
||||
}
|
||||
}
|
324
MySqlPlus/MySqlManagedConnection.cs
Normal file
324
MySqlPlus/MySqlManagedConnection.cs
Normal file
@ -0,0 +1,324 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
/// <summary>
|
||||
/// The outline of a managed MySqlConnection that adds extra functionality like automatic
|
||||
/// query retrying and reconnecting if the remote DB goes away or becomes busy.
|
||||
/// </summary>
|
||||
public class MySqlManagedConnection : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The Internal MySqlConnection for this managed copy.
|
||||
/// </summary>
|
||||
public MySqlConnection Internal = null;
|
||||
|
||||
/// <summary>
|
||||
/// The connection string that is used to repoen the connection.
|
||||
/// </summary>
|
||||
private string ConnectionString = null;
|
||||
|
||||
/// <summary>
|
||||
/// Wether or not we are connected to the remote server.
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Internal == null || this.Internal.State != System.Data.ConnectionState.Open || !this.Internal.Ping())
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new managed MySqlConnection and sets its internal
|
||||
/// MySql connection.
|
||||
/// </summary>
|
||||
/// <param name="conn"></param>
|
||||
public MySqlManagedConnection(MySqlConnection conn)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(conn.ConnectionString))
|
||||
throw new Exception("Connection string must be set on MySqlConnection.");
|
||||
|
||||
this.Internal = conn;
|
||||
this.ConnectionString = conn.ConnectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new managed MySqlConnection and setups up the internal connection string and gets everything ready.
|
||||
/// </summary>
|
||||
/// <param name="conn"></param>
|
||||
public MySqlManagedConnection(string conn)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(conn))
|
||||
throw new Exception("Invalid connection string passed, it must not be null or empty.");
|
||||
|
||||
this.Internal = new MySqlConnection(conn);
|
||||
this.ConnectionString = conn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to reconnect to the server and retrys if needed.
|
||||
/// </summary>
|
||||
public void Reconnect(int maxRetrys = 5, bool exponentialBackoff = true, bool retry = true)
|
||||
{
|
||||
int backoffSleep = 2000;
|
||||
for (int i = 0; i < maxRetrys; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Internal == null)
|
||||
{
|
||||
Internal = new MySqlConnection(this.ConnectionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
try { Internal.Close(); } catch { }
|
||||
|
||||
try { Internal.Dispose(); } catch { }
|
||||
|
||||
Internal = new MySqlConnection(this.ConnectionString);
|
||||
}
|
||||
|
||||
Internal.Open();
|
||||
|
||||
//If we are now connected then stop trying.
|
||||
if (this.IsConnected)
|
||||
break;
|
||||
else
|
||||
throw new Exception("Failed to open a valid MySqlConnection.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//See if we have reached our max retry.
|
||||
if (i + 1 >= maxRetrys || !retry)
|
||||
throw;
|
||||
|
||||
//If not backoff for a bit and see if the server comes back online.
|
||||
if (exponentialBackoff)
|
||||
{
|
||||
Thread.Sleep(backoffSleep);
|
||||
backoffSleep *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a connection the remote server if we can.
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
//Just invoke reconnect it handles everything for us.
|
||||
this.Reconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this managed connection.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (Internal != null)
|
||||
{
|
||||
try { Internal.Close(); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a data reader for a command and retrys
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="maxRetrys"></param>
|
||||
/// <param name="exponentialBackoff"></param>
|
||||
/// <param name="retry"></param>
|
||||
/// <returns></returns>
|
||||
public MySqlDataReader ExecuteReader(MySqlCommand command, int maxRetrys = 5, bool exponentialBackoff = true, bool retry = true)
|
||||
{
|
||||
int backoffSleep = 2000;
|
||||
command.CommandTimeout = 60; //Time in seconds
|
||||
|
||||
for (int i = 0; i < maxRetrys; i++)
|
||||
{
|
||||
ulong startTimestamp = GlobalTimeStamp.Current;
|
||||
|
||||
try
|
||||
{
|
||||
command.Connection = this.Internal;
|
||||
return command.ExecuteReader();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Get the inner most exception.
|
||||
var innerException = ex;
|
||||
while (innerException.InnerException != null)
|
||||
innerException = innerException.InnerException;
|
||||
|
||||
//Try to get the MySql error code
|
||||
int code = -1;
|
||||
if (ex is MySqlException)
|
||||
code = ((MySqlException)ex).Number;
|
||||
|
||||
//See if we have reached our max retry.
|
||||
if (i + 1 >= maxRetrys || !retry)
|
||||
throw;
|
||||
|
||||
//See if the connection was invalid
|
||||
if (innerException.GetType().IsAssignableFrom(typeof(System.Net.Sockets.SocketException)) ||
|
||||
innerException.GetType().IsAssignableFrom(typeof(MySqlConnection)) ||
|
||||
ex.GetType().IsAssignableFrom(typeof(MySqlConnection)) ||
|
||||
ex.Message.ToLower().Contains("connection must be valid and open") ||
|
||||
ex.Message.ToLower().Contains("a connection attempt failed because the connected party did not properly respond after a period of time") ||
|
||||
ex.Message.ToLower().Contains("an existing connection was forcibly closed by the remote host"))
|
||||
{
|
||||
//Attempt to reopen the connection.
|
||||
this.Reconnect(maxRetrys, exponentialBackoff);
|
||||
}
|
||||
|
||||
//See if we should retry or just throw.
|
||||
if (!ShouldRetryBasedOnMySqlErrorNum(code))
|
||||
throw;
|
||||
|
||||
//If the operation took less than 5 seconds, then sleep. Otherwise continue right away. This is to prevent OpenRetry from making us wait even longer.
|
||||
if (GlobalTimeStamp.GetDifference(startTimestamp) <= 5000 && exponentialBackoff)
|
||||
{
|
||||
Thread.Sleep(backoffSleep);
|
||||
backoffSleep *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"MySql ExecuteReader failed. Max timeout reached. Query: {command.CommandText}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a mysql command without a reader.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="maxRetrys"></param>
|
||||
/// <param name="exponentialBackoff"></param>
|
||||
/// <param name="retry"></param>
|
||||
public int ExecuteNonQuery(MySqlCommand command, int maxRetrys = 5, bool exponentialBackoff = true, bool retry = true)
|
||||
{
|
||||
int backoffSleep = 2000;
|
||||
command.CommandTimeout = 60; // time in seconds
|
||||
|
||||
for (int i = 0; i < maxRetrys; i++)
|
||||
{
|
||||
ulong startTimestamp = GlobalTimeStamp.Current;
|
||||
|
||||
try
|
||||
{
|
||||
command.Connection = this.Internal;
|
||||
|
||||
return command.ExecuteNonQuery();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Get the inner most exception.
|
||||
var innerException = ex;
|
||||
while (innerException.InnerException != null)
|
||||
innerException = innerException.InnerException;
|
||||
|
||||
//Try to get the MySql error code if we can
|
||||
int code = -1;
|
||||
if (ex is MySqlException)
|
||||
code = ((MySqlException)ex).Number;
|
||||
|
||||
//See if we have reached our max retry.
|
||||
if (i + 1 >= maxRetrys || !retry)
|
||||
throw;
|
||||
|
||||
//See if the connection was invalid
|
||||
if (innerException.GetType().IsAssignableFrom(typeof(System.Net.Sockets.SocketException)) ||
|
||||
innerException.GetType().IsAssignableFrom(typeof(MySqlConnection)) ||
|
||||
ex.GetType().IsAssignableFrom(typeof(MySqlConnection)) ||
|
||||
ex.Message.ToLower().Contains("connection must be valid and open") ||
|
||||
ex.Message.ToLower().Contains("a connection attempt failed because the connected party did not properly respond after a period of time") ||
|
||||
ex.Message.ToLower().Contains("an existing connection was forcibly closed by the remote host"))
|
||||
{
|
||||
this.Reconnect(maxRetrys, exponentialBackoff);
|
||||
}
|
||||
|
||||
//See if we should retry or just throw.
|
||||
if (!ShouldRetryBasedOnMySqlErrorNum(code))
|
||||
throw;
|
||||
|
||||
//If the operation took less than 5 seconds, then sleep. Otherwise continue right away.
|
||||
if (GlobalTimeStamp.GetDifference(startTimestamp) <= 5000 && exponentialBackoff)
|
||||
{
|
||||
Thread.Sleep(backoffSleep);
|
||||
backoffSleep *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"MySql ExecuteNonQuery failed. Max timeout reached. Query: {command.CommandText}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether or not we should retry a query based on the MySql error number.
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <returns></returns>
|
||||
private static bool ShouldRetryBasedOnMySqlErrorNum(int number)
|
||||
{
|
||||
//List of codes here: https://www.briandunning.com/error-codes/?source=MySQL
|
||||
if (number >= 1044 && number <= 1052)
|
||||
return false;
|
||||
else if (number >= 1054 && number <= 1075)
|
||||
return false;
|
||||
else if (number == 1265)
|
||||
return false; //Data truncation (Don't try again)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this managed connection and releases all resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Internal != null)
|
||||
{
|
||||
try { Internal.Close(); } catch { }
|
||||
|
||||
try { Internal.Dispose(); } catch { }
|
||||
|
||||
Internal = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this managed connections needs to be deallocated.
|
||||
///
|
||||
/// </summary>
|
||||
~MySqlManagedConnection()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the exposal of the internal MySqlConnection from the managed version.
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
public static implicit operator MySqlConnection(MySqlManagedConnection connection)
|
||||
{
|
||||
return connection.Internal;
|
||||
}
|
||||
}
|
||||
}
|
15
MySqlPlus/MySqlPlus.csproj
Normal file
15
MySqlPlus/MySqlPlus.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AssemblyName>MontoyaTech.MySqlPlus</AssemblyName>
|
||||
<RootNamespace>MontoyaTech.MySqlPlus</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="8.0.32" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
12
MySqlPlus/MySqlRow.cs
Normal file
12
MySqlPlus/MySqlRow.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
public class MySqlRow
|
||||
{
|
||||
}
|
||||
}
|
13
MySqlPlus/MySqlRowId.cs
Normal file
13
MySqlPlus/MySqlRowId.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
public class MySqlRowId : Attribute
|
||||
{
|
||||
public MySqlRowId() { }
|
||||
}
|
||||
}
|
128
MySqlPlus/MySqlSession.cs
Normal file
128
MySqlPlus/MySqlSession.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using Microsoft.VisualBasic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
/// <summary>
|
||||
/// The outline of a MySqlSession which contains a MySqlManagedConnection
|
||||
/// and a few other help functions.
|
||||
/// </summary>
|
||||
public class MySqlSession : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns whether or not this MySqlSession has a valid connection.
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Connection == null || !this.Connection.IsConnected)
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The underlying MySql Connection that this Session is using.
|
||||
/// </summary>
|
||||
public MySqlManagedConnection Connection = null;
|
||||
|
||||
/// <summary>
|
||||
/// The raw connection string used to open a MySqlConnection.
|
||||
/// </summary>
|
||||
protected string ConnectionStr = null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new MySqlSession with a connection string.
|
||||
/// </summary>
|
||||
/// <param name="connectionStr"></param>
|
||||
public MySqlSession(string connectionStr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(this.ConnectionStr))
|
||||
throw new Exception("Must provide a valid non null or empty MySql connection string.");
|
||||
|
||||
this.ConnectionStr = connectionStr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to connect to the MySql Server using the connection information.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public void Connect()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
//Dont connect again if we are already connected.
|
||||
if (this.Connection == null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(this.ConnectionStr))
|
||||
{
|
||||
try
|
||||
{
|
||||
//Setup the mysql connection.
|
||||
this.Connection = new MySqlManagedConnection(this.ConnectionStr);
|
||||
|
||||
//Attempt to open the connection, this will make sure its valid and works for us.
|
||||
this.Connection.Open();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Failed to connect to MySql database.", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Missing connection details to connect to MySql database.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects from the MySql Server this Session is connected to.
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Connection != null)
|
||||
{
|
||||
try { this.Connection.Close(); } catch { }
|
||||
|
||||
try { this.Connection.Dispose(); } catch { }
|
||||
|
||||
this.Connection = null;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.Connection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~MySqlSession()
|
||||
{
|
||||
this.Disconnect();
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
this.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
264
MySqlPlus/MySqlSessionCache.cs
Normal file
264
MySqlPlus/MySqlSessionCache.cs
Normal file
@ -0,0 +1,264 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MontoyaTech.MySqlPlus
|
||||
{
|
||||
/// <summary>
|
||||
/// The outline of a MySqlSession Cache which will cache and automatically free MySql Sessions
|
||||
/// in order to speed up query times.
|
||||
/// </summary>
|
||||
public class MySqlSessionCache
|
||||
{
|
||||
/// <summary>
|
||||
/// The outline of a cached MySqlSession.
|
||||
/// </summary>
|
||||
public class CachedMySqlSession : MySqlSession
|
||||
{
|
||||
/// <summary>
|
||||
/// The Last Time this cached API Session was used.
|
||||
/// </summary>
|
||||
public DateTime LastUse = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// The method that requested this cache.
|
||||
/// </summary>
|
||||
public string CallerMethod = "";
|
||||
|
||||
/// <summary>
|
||||
/// The line of code that requested this cache.
|
||||
/// </summary>
|
||||
public string CallerLine = "";
|
||||
|
||||
/// <summary>
|
||||
/// The Id of this CachedAPISession.
|
||||
/// </summary>
|
||||
public string Id = "";
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this Cached API Session is in use.
|
||||
/// </summary>
|
||||
public bool InUse = false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new default CachedAPISession.
|
||||
/// </summary>
|
||||
public CachedMySqlSession(string connectionStr) : base(connectionStr)
|
||||
{
|
||||
this.Id = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dont allow the Session to be killed. Instead set in use to false.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
InUse = false;
|
||||
|
||||
//Set this again because we just stopped using this cached session.
|
||||
this.LastUse = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The connection string to use when creating MySqlSessions.
|
||||
/// </summary>
|
||||
private static string ConnectionStr = null;
|
||||
|
||||
/// <summary>
|
||||
/// The list of Cached Sessions currently being maintained by the Cache.
|
||||
/// </summary>
|
||||
private static List<CachedMySqlSession> Sessions = new List<CachedMySqlSession>();
|
||||
|
||||
/// <summary>
|
||||
/// The Thread that will handle uncaching.
|
||||
/// </summary>
|
||||
private static Thread UnCacheThread = null;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time in minutes that a cached api session is allowed to live.
|
||||
/// </summary>
|
||||
private static int CacheLifeTime = 20;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time in minutes before a cache is considered to be a zombie
|
||||
/// and the result of a code bug somewhere.
|
||||
/// </summary>
|
||||
private static int ZombieCacheLifeTime = 5;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time in seconds before we are allowed to attempt to uncache sessions.
|
||||
/// </summary>
|
||||
private static int UnCacheFrequency = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the API Session Cache is running.
|
||||
/// </summary>
|
||||
private static bool Running = false;
|
||||
|
||||
/// <summary>
|
||||
/// Starts this MySqlSessionCache with a given MySqlConnectionString.
|
||||
/// </summary>
|
||||
/// <param name="connectionStr"></param>
|
||||
public static void Start(string connectionStr)
|
||||
{
|
||||
ConnectionStr = connectionStr;
|
||||
|
||||
lock (Sessions)
|
||||
{
|
||||
if (!Running)
|
||||
{
|
||||
Running = true;
|
||||
|
||||
UnCacheThread = new Thread(UnCache);
|
||||
UnCacheThread.IsBackground = true;
|
||||
UnCacheThread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the APISessionCache Service.
|
||||
/// </summary>
|
||||
public static void Stop()
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
if (Running)
|
||||
{
|
||||
UnCacheThread = null;
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < Sessions.Count; i++)
|
||||
{
|
||||
Sessions[i].Disconnect();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Sessions = null;
|
||||
|
||||
Running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills all the cached api sessions if we can.
|
||||
/// </summary>
|
||||
public static void KillCache()
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
for (int i = 0; i < Sessions.Count; i++)
|
||||
{
|
||||
var session = Sessions[i];
|
||||
|
||||
//Remove the sessions not in use and the ones who are zombies.
|
||||
|
||||
if (session.InUse == false)
|
||||
{
|
||||
Sessions.RemoveAt(i);
|
||||
session.Disconnect();
|
||||
i--;
|
||||
}
|
||||
else if (session.InUse && (DateTime.UtcNow - session.LastUse).Minutes >= ZombieCacheLifeTime)
|
||||
{
|
||||
Sessions.RemoveAt(i);
|
||||
session.Disconnect();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a APISession that is either new or cached depending on whats available.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CachedMySqlSession Get()
|
||||
{
|
||||
if (!Running)
|
||||
throw new Exception("API Session Cache is not running!");
|
||||
|
||||
lock (Sessions)
|
||||
{
|
||||
CachedMySqlSession result = null;
|
||||
|
||||
//See if we can find an existing session to use.
|
||||
foreach (var cached in Sessions)
|
||||
{
|
||||
if (cached.InUse == false)
|
||||
{
|
||||
result = cached;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Create a new Session if we didnt find one.
|
||||
if (result == null)
|
||||
{
|
||||
result = new CachedMySqlSession(ConnectionStr);
|
||||
result.Connect();
|
||||
Sessions.Add(result);
|
||||
}
|
||||
|
||||
result.InUse = true;
|
||||
result.LastUse = DateTime.UtcNow;
|
||||
|
||||
//Get the caller information
|
||||
var trace = new StackTrace();
|
||||
|
||||
var method = trace.GetFrame(1).GetMethod();
|
||||
var methodName = method.Name;
|
||||
var className = method.ReflectedType.Name;
|
||||
var currNamespace = method.ReflectedType.Namespace;
|
||||
|
||||
result.CallerMethod = $"{currNamespace}.{className}.{methodName}()";
|
||||
result.CallerLine = trace.GetFrame(1).GetFileLineNumber().ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The code that handles uncaching api sessions.
|
||||
/// </summary>
|
||||
private static void UnCache()
|
||||
{
|
||||
while (Running)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
for (int i = 0; i < Sessions.Count; i++)
|
||||
{
|
||||
var session = Sessions[i];
|
||||
|
||||
//If this session is older than it's lifetime and it's not in use, kill it.
|
||||
if (session.InUse == false && (DateTime.UtcNow - session.LastUse).Minutes >= CacheLifeTime)
|
||||
{
|
||||
Sessions.RemoveAt(i);
|
||||
session.Disconnect();
|
||||
i--;
|
||||
}
|
||||
//If this session is in use but it appears to be a zombie session, kill it.
|
||||
else if (session.InUse && (DateTime.UtcNow - session.LastUse).Minutes >= ZombieCacheLifeTime)
|
||||
{
|
||||
Sessions.RemoveAt(i);
|
||||
session.Disconnect();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(1000 * UnCacheFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user