Sicherung gnaz neu

This commit is contained in:
Maier Stephan SI
2023-02-15 00:55:50 +01:00
parent 56c25672f9
commit f3f89b94f5
1348 changed files with 113234 additions and 98368 deletions

390
FSI.Lib/.gitignore vendored
View File

@@ -1,390 +0,0 @@
# ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.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
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# 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
# Note: 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
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Nuget personal access tokens and Credentials
nuget.config
# 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
*.appx
*.appxbundle
*.appxupload
# 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
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# 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/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
.idea/
*.sln.iml

View File

@@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSI.Lib", "FSI.Lib\FSI.Lib.csproj", "{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0198E104-F95E-49D7-8FA5-9366011FAA9A}
EndGlobalSection
EndGlobal

View File

@@ -1,608 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace FSI.Lib.Audio
{
/// <summary>
/// Controls audio using the Windows CoreAudio API
/// from: http://stackoverflow.com/questions/14306048/controling-volume-mixer
/// and: http://netcoreaudio.codeplex.com/
/// </summary>
public static class AudioManager
{
#region Master Volume Manipulation
/// <summary>
/// Gets the current master volume in scalar values (percentage)
/// </summary>
/// <returns>-1 in case of an error, if successful the value will be between 0 and 100</returns>
public static float GetMasterVolume()
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return -1;
masterVol.GetMasterVolumeLevelScalar(out float volumeLevel);
return volumeLevel * 100;
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
/// <summary>
/// Gets the mute state of the master volume.
/// While the volume can be muted the <see cref="GetMasterVolume"/> will still return the pre-muted volume value.
/// </summary>
/// <returns>false if not muted, true if volume is muted</returns>
public static bool GetMasterVolumeMute()
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return false;
masterVol.GetMute(out bool isMuted);
return isMuted;
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
/// <summary>
/// Sets the master volume to a specific level
/// </summary>
/// <param name="newLevel">Value between 0 and 100 indicating the desired scalar value of the volume</param>
public static void SetMasterVolume(float newLevel)
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return;
masterVol.SetMasterVolumeLevelScalar(newLevel / 100, Guid.Empty);
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
/// <summary>
/// Increments or decrements the current volume level by the <see cref="stepAmount"/>.
/// </summary>
/// <param name="stepAmount">Value between -100 and 100 indicating the desired step amount. Use negative numbers to decrease
/// the volume and positive numbers to increase it.</param>
/// <returns>the new volume level assigned</returns>
public static float StepMasterVolume(float stepAmount)
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return -1;
float stepAmountScaled = stepAmount / 100;
// Get the level
masterVol.GetMasterVolumeLevelScalar(out float volumeLevel);
// Calculate the new level
float newLevel = volumeLevel + stepAmountScaled;
newLevel = Math.Min(1, newLevel);
newLevel = Math.Max(0, newLevel);
masterVol.SetMasterVolumeLevelScalar(newLevel, Guid.Empty);
// Return the new volume level that was set
return newLevel * 100;
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
/// <summary>
/// Mute or unmute the master volume
/// </summary>
/// <param name="isMuted">true to mute the master volume, false to unmute</param>
public static void SetMasterVolumeMute(bool isMuted)
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return;
masterVol.SetMute(isMuted, Guid.Empty);
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
/// <summary>
/// Switches between the master volume mute states depending on the current state
/// </summary>
/// <returns>the current mute state, true if the volume was muted, false if unmuted</returns>
public static bool ToggleMasterVolumeMute()
{
IAudioEndpointVolume masterVol = null;
try
{
masterVol = GetMasterVolumeObject();
if (masterVol == null)
return false;
masterVol.GetMute(out bool isMuted);
masterVol.SetMute(!isMuted, Guid.Empty);
return !isMuted;
}
finally
{
if (masterVol != null)
Marshal.ReleaseComObject(masterVol);
}
}
private static IAudioEndpointVolume GetMasterVolumeObject()
{
IMMDeviceEnumerator deviceEnumerator = null;
IMMDevice speakers = null;
try
{
deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
Guid IID_IAudioEndpointVolume = typeof(IAudioEndpointVolume).GUID;
speakers.Activate(ref IID_IAudioEndpointVolume, 0, IntPtr.Zero, out object o);
IAudioEndpointVolume masterVol = (IAudioEndpointVolume)o;
return masterVol;
}
finally
{
if (speakers != null) Marshal.ReleaseComObject(speakers);
if (deviceEnumerator != null) Marshal.ReleaseComObject(deviceEnumerator);
}
}
#endregion
#region Individual Application Volume Manipulation
public static float? GetApplicationVolume(int pid)
{
ISimpleAudioVolume volume = GetVolumeObject(pid);
if (volume == null)
return null;
volume.GetMasterVolume(out float level);
Marshal.ReleaseComObject(volume);
return level * 100;
}
public static bool? GetApplicationMute(int pid)
{
ISimpleAudioVolume volume = GetVolumeObject(pid);
if (volume == null)
return null;
volume.GetMute(out bool mute);
Marshal.ReleaseComObject(volume);
return mute;
}
public static void SetApplicationVolume(int pid, float level)
{
ISimpleAudioVolume volume = GetVolumeObject(pid);
if (volume == null)
return;
Guid guid = Guid.Empty;
volume.SetMasterVolume(level / 100, ref guid);
Marshal.ReleaseComObject(volume);
}
public static void SetApplicationMute(int pid, bool mute)
{
ISimpleAudioVolume volume = GetVolumeObject(pid);
if (volume == null)
return;
Guid guid = Guid.Empty;
volume.SetMute(mute, ref guid);
Marshal.ReleaseComObject(volume);
}
private static ISimpleAudioVolume GetVolumeObject(int pid)
{
IMMDeviceEnumerator deviceEnumerator = null;
IAudioSessionEnumerator sessionEnumerator = null;
IAudioSessionManager2 mgr = null;
IMMDevice speakers = null;
try
{
// get the speakers (1st render + multimedia) device
deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
// activate the session manager. we need the enumerator
Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out object o);
mgr = (IAudioSessionManager2)o;
// enumerate sessions for on this device
mgr.GetSessionEnumerator(out sessionEnumerator);
sessionEnumerator.GetCount(out int count);
// search for an audio session with the required process-id
ISimpleAudioVolume volumeControl = null;
for (int i = 0; i < count; ++i)
{
IAudioSessionControl2 ctl = null;
try
{
sessionEnumerator.GetSession(i, out ctl);
// NOTE: we could also use the app name from ctl.GetDisplayName()
ctl.GetProcessId(out int cpid);
if (cpid == pid)
{
volumeControl = ctl as ISimpleAudioVolume;
break;
}
}
finally
{
if (ctl != null) Marshal.ReleaseComObject(ctl);
}
}
return volumeControl;
}
finally
{
if (sessionEnumerator != null) Marshal.ReleaseComObject(sessionEnumerator);
if (mgr != null) Marshal.ReleaseComObject(mgr);
if (speakers != null) Marshal.ReleaseComObject(speakers);
if (deviceEnumerator != null) Marshal.ReleaseComObject(deviceEnumerator);
}
}
#endregion
}
#region Abstracted COM interfaces from Windows CoreAudio API
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
internal class MMDeviceEnumerator
{
}
internal enum EDataFlow
{
eRender,
eCapture,
eAll,
EDataFlow_enum_count
}
internal enum ERole
{
eConsole,
eMultimedia,
eCommunications,
ERole_enum_count
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceEnumerator
{
int NotImpl1();
[PreserveSig]
int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
// the rest is not implemented
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDevice
{
[PreserveSig]
int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
// the rest is not implemented
}
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionManager2
{
int NotImpl1();
int NotImpl2();
[PreserveSig]
int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);
// the rest is not implemented
}
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionEnumerator
{
[PreserveSig]
int GetCount(out int SessionCount);
[PreserveSig]
int GetSession(int SessionCount, out IAudioSessionControl2 Session);
}
[Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISimpleAudioVolume
{
[PreserveSig]
int SetMasterVolume(float fLevel, ref Guid EventContext);
[PreserveSig]
int GetMasterVolume(out float pfLevel);
[PreserveSig]
int SetMute(bool bMute, ref Guid EventContext);
[PreserveSig]
int GetMute(out bool pbMute);
}
[Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionControl2
{
// IAudioSessionControl
[PreserveSig]
int NotImpl0();
[PreserveSig]
int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
[PreserveSig]
int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int GetIconPath([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
[PreserveSig]
int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int GetGroupingParam(out Guid pRetVal);
[PreserveSig]
int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int NotImpl1();
[PreserveSig]
int NotImpl2();
// IAudioSessionControl2
[PreserveSig]
int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
[PreserveSig]
int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
[PreserveSig]
int GetProcessId(out int pRetVal);
[PreserveSig]
int IsSystemSoundsSession();
[PreserveSig]
int SetDuckingPreference(bool optOut);
}
// http://netcoreaudio.codeplex.com/SourceControl/latest#trunk/Code/CoreAudio/Interfaces/IAudioEndpointVolume.cs
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAudioEndpointVolume
{
[PreserveSig]
int NotImpl1();
[PreserveSig]
int NotImpl2();
/// <summary>
/// Gets a count of the channels in the audio stream.
/// </summary>
/// <param name="channelCount">The number of channels.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetChannelCount(
[Out][MarshalAs(UnmanagedType.U4)] out UInt32 channelCount);
/// <summary>
/// Sets the master volume level of the audio stream, in decibels.
/// </summary>
/// <param name="level">The new master volume level in decibels.</param>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int SetMasterVolumeLevel(
[In][MarshalAs(UnmanagedType.R4)] float level,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Sets the master volume level, expressed as a normalized, audio-tapered value.
/// </summary>
/// <param name="level">The new master volume level expressed as a normalized value between 0.0 and 1.0.</param>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int SetMasterVolumeLevelScalar(
[In][MarshalAs(UnmanagedType.R4)] float level,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Gets the master volume level of the audio stream, in decibels.
/// </summary>
/// <param name="level">The volume level in decibels.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetMasterVolumeLevel(
[Out][MarshalAs(UnmanagedType.R4)] out float level);
/// <summary>
/// Gets the master volume level, expressed as a normalized, audio-tapered value.
/// </summary>
/// <param name="level">The volume level expressed as a normalized value between 0.0 and 1.0.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetMasterVolumeLevelScalar(
[Out][MarshalAs(UnmanagedType.R4)] out float level);
/// <summary>
/// Sets the volume level, in decibels, of the specified channel of the audio stream.
/// </summary>
/// <param name="channelNumber">The channel number.</param>
/// <param name="level">The new volume level in decibels.</param>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int SetChannelVolumeLevel(
[In][MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
[In][MarshalAs(UnmanagedType.R4)] float level,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Sets the normalized, audio-tapered volume level of the specified channel in the audio stream.
/// </summary>
/// <param name="channelNumber">The channel number.</param>
/// <param name="level">The new master volume level expressed as a normalized value between 0.0 and 1.0.</param>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int SetChannelVolumeLevelScalar(
[In][MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
[In][MarshalAs(UnmanagedType.R4)] float level,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Gets the volume level, in decibels, of the specified channel in the audio stream.
/// </summary>
/// <param name="channelNumber">The zero-based channel number.</param>
/// <param name="level">The volume level in decibels.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetChannelVolumeLevel(
[In][MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
[Out][MarshalAs(UnmanagedType.R4)] out float level);
/// <summary>
/// Gets the normalized, audio-tapered volume level of the specified channel of the audio stream.
/// </summary>
/// <param name="channelNumber">The zero-based channel number.</param>
/// <param name="level">The volume level expressed as a normalized value between 0.0 and 1.0.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetChannelVolumeLevelScalar(
[In][MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
[Out][MarshalAs(UnmanagedType.R4)] out float level);
/// <summary>
/// Sets the muting state of the audio stream.
/// </summary>
/// <param name="isMuted">True to mute the stream, or false to unmute the stream.</param>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int SetMute(
[In][MarshalAs(UnmanagedType.Bool)] Boolean isMuted,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Gets the muting state of the audio stream.
/// </summary>
/// <param name="isMuted">The muting state. True if the stream is muted, false otherwise.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetMute(
[Out][MarshalAs(UnmanagedType.Bool)] out Boolean isMuted);
/// <summary>
/// Gets information about the current step in the volume range.
/// </summary>
/// <param name="step">The current zero-based step index.</param>
/// <param name="stepCount">The total number of steps in the volume range.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetVolumeStepInfo(
[Out][MarshalAs(UnmanagedType.U4)] out UInt32 step,
[Out][MarshalAs(UnmanagedType.U4)] out UInt32 stepCount);
/// <summary>
/// Increases the volume level by one step.
/// </summary>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int VolumeStepUp(
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Decreases the volume level by one step.
/// </summary>
/// <param name="eventContext">A user context value that is passed to the notification callback.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int VolumeStepDown(
[In][MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
/// <summary>
/// Queries the audio endpoint device for its hardware-supported functions.
/// </summary>
/// <param name="hardwareSupportMask">A hardware support mask that indicates the capabilities of the endpoint.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int QueryHardwareSupport(
[Out][MarshalAs(UnmanagedType.U4)] out UInt32 hardwareSupportMask);
/// <summary>
/// Gets the volume range of the audio stream, in decibels.
/// </summary>
/// <param name="volumeMin">The minimum volume level in decibels.</param>
/// <param name="volumeMax">The maximum volume level in decibels.</param>
/// <param name="volumeStep">The volume increment level in decibels.</param>
/// <returns>An HRESULT code indicating whether the operation passed of failed.</returns>
[PreserveSig]
int GetVolumeRange(
[Out][MarshalAs(UnmanagedType.R4)] out float volumeMin,
[Out][MarshalAs(UnmanagedType.R4)] out float volumeMax,
[Out][MarshalAs(UnmanagedType.R4)] out float volumeStep);
}
#endregion
}

View File

@@ -1,208 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Cache for properties, fields, and methods to speed up reflection
/// </summary>
internal static class Cache
{
/// <summary>
/// Reflection Cache for property info
/// </summary>
private static readonly Dictionary<Type, PropertyInfo[]> _propertyCache;
/// <summary>
/// Reflection Cache for field info
/// </summary>
private static readonly Dictionary<Type, FieldInfo[]> _fieldCache;
/// <summary>
/// Reflection Cache for methods
/// </summary>
private static readonly Dictionary<Type, MethodInfo[]> _methodList;
/// <summary>
/// Static constructor
/// </summary>
static Cache()
{
_propertyCache = new Dictionary<Type, PropertyInfo[]>();
_fieldCache = new Dictionary<Type, FieldInfo[]>();
_methodList = new Dictionary<Type, MethodInfo[]>();
}
/// <summary>
/// Clear the cache
/// </summary>
public static void ClearCache()
{
lock(_propertyCache)
_propertyCache.Clear();
lock(_fieldCache)
_fieldCache.Clear();
lock(_methodList)
_methodList.Clear();
}
/// <summary>
/// Get a list of the fields within a type
/// </summary>
/// <param name="config"> </param>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<FieldInfo> GetFieldInfo(ComparisonConfig config, Type type)
{
lock (_fieldCache)
{
bool isDynamicType = TypeHelper.IsDynamicObject(type);
if (config.Caching && _fieldCache.ContainsKey(type))
return _fieldCache[type];
FieldInfo[] currentFields;
#if !NETSTANDARD1_3
//All the implementation examples that I have seen for dynamic objects use private fields or properties
if (( config.ComparePrivateFields || isDynamicType) && !config.CompareStaticFields)
{
List<FieldInfo> list = new List<FieldInfo>();
Type t = type;
do
{
list.AddRange(t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
t = t.BaseType;
} while (t != null);
currentFields = list.ToArray();
}
else if ((config.ComparePrivateFields || isDynamicType) && config.CompareStaticFields)
{
List<FieldInfo> list = new List<FieldInfo>();
Type t = type;
do
{
list.AddRange(
t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Static));
t = t.BaseType;
} while (t != null);
currentFields = list.ToArray();
}
else
#endif
currentFields = type.GetFields(); //Default is public instance and static
if (config.Caching)
_fieldCache.Add(type, currentFields);
return currentFields;
}
}
/// <summary>
/// Get the value of a property
/// </summary>
/// <param name="config"> </param>
/// <param name="type"></param>
/// <param name="objectValue"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static object GetPropertyValue(ComparisonConfig config, Type type, object objectValue, string propertyName)
{
lock (_propertyCache)
return GetPropertyInfo(config, type).First(o => o.Name == propertyName).GetValue(objectValue, null);
}
/// <summary>
/// Get a list of the properties in a type
/// </summary>
/// <param name="config"> </param>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<PropertyInfo> GetPropertyInfo(ComparisonConfig config, Type type)
{
lock (_propertyCache)
{
bool isDynamicType = TypeHelper.IsDynamicObject(type);
if (config.Caching && _propertyCache.ContainsKey(type))
return _propertyCache[type];
PropertyInfo[] currentProperties;
#if NETSTANDARD1_3
if (!config.CompareStaticProperties)
currentProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
else
currentProperties = type.GetProperties(); //Default is public instance and static
#else
//All the implementation examples that I have seen for dynamic objects use private fields or properties
if ((config.ComparePrivateProperties || isDynamicType) && !config.CompareStaticProperties)
{
currentProperties =
type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
else if ((config.ComparePrivateProperties || isDynamicType) && config.CompareStaticProperties)
{
currentProperties =
type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Static);
}
else if (!config.CompareStaticProperties)
{
currentProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
}
else
{
currentProperties = type.GetProperties(); //Default is public instance and static
}
#endif
if (config.Caching)
{
_propertyCache.Add(type, currentProperties);
}
return currentProperties;
}
}
/// <summary>
/// Get a method by name
/// </summary>
/// <param name="type"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public static MethodInfo GetMethod(Type type, string methodName)
{
lock (_methodList)
return GetMethods(type).FirstOrDefault(m => m.Name == methodName);
}
/// <summary>
/// Get the cached methods for a type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<MethodInfo> GetMethods(Type type)
{
lock (_methodList)
{
if (_methodList.ContainsKey(type))
return _methodList[type];
MethodInfo[] myMethodInfo = type.GetMethods();
_methodList.Add(type, myMethodInfo);
return myMethodInfo;
}
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// CompareException with a Result Property
/// </summary>
public class CompareException : Exception
{
/// <summary>
/// CompareException Constructor
/// </summary>
/// <param name="result"></param>
/// <param name="message"></param>
public CompareException(ComparisonResult result, string message) : base(message)
{
Result = result;
}
/// <summary>
/// The comparison Result
/// </summary>
public ComparisonResult Result { get; private set; }
}
}

View File

@@ -1,71 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// A set of BDD style comparison extensions for use with Testing Frameworks
/// </summary>
public static class CompareExtensions
{
static CompareExtensions()
{
Config = new ComparisonConfig();
}
/// <summary>
/// Alter the configuration for the comparison
/// </summary>
public static ComparisonConfig Config { get; set; }
/// <summary>
/// Throws a CompareException if the classes are not equal
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="actual"></param>
/// <param name="expected"></param>
/// <param name="message"></param>
/// <param name="compareConfig"></param>
public static void ShouldCompare<T>(this T actual, T expected, string message = null, ComparisonConfig compareConfig = null)
{
var logic = new CompareLogic(compareConfig ?? Config);
ComparisonResult result = logic.Compare(expected, actual);
if (!result.AreEqual)
{
throw new CompareException(result, BuildExpectedEqualMessage(message,result));
}
}
/// <summary>
/// Throws a CompareException if the classes are equal
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="actual"></param>
/// <param name="expected"></param>
/// <param name="message"></param>
/// <param name="compareConfig"></param>
public static void ShouldNotCompare<T>(this T actual, T expected, string message = null, ComparisonConfig compareConfig = null)
{
var logic = new CompareLogic(compareConfig ?? Config);
ComparisonResult result = logic.Compare(expected, actual);
if (result.AreEqual)
{
throw new CompareException(result, BuildExpectedNotEqualMessage(message, result));
}
}
private static string BuildExpectedEqualMessage(string message, ComparisonResult result)
{
message = message ?? "Objects expected to be equal";
return message + Environment.NewLine + result.DifferencesString + Environment.NewLine;
}
private static string BuildExpectedNotEqualMessage(string message, ComparisonResult result)
{
message = message ?? "Objects expected NOT to be equal";
return message + Environment.NewLine + result.DifferencesString + Environment.NewLine;
}
}
}

View File

@@ -1,315 +0,0 @@
// This software is provided free of charge from Kellerman Software.
// It may be used in any project, including commercial for sale projects.
//
// Check out our other great software at:
// http://www.FSI.Lib.com
// * Free Quick Reference Pack for Developers
// * Free Sharp Zip Wrapper
// * .NET Caching Library
// * .NET Code Generator
// * .NET Email Validation Library
// * .NET Encryption Library
// * .NET Excel Reports (Create Excel reports without excel being installed)
// * .NET FTP Library
// * .NET Link Tracker
// * .NET Logging Library
// * .NET PGP Library
// * .NET SFTP Library
// * .NET Word Reports (Create reports based on Microsoft Word files without having Microsoft Word installed)
// * AccessDiff (Detects data, code, and form differences)
// * Cheap Reports for Shopify
// * Cheap Taxes for Shopify
// * Connection String Creator
// * Config Helper Pro (Read and write to the registry, config files, and INI files with 100% managed code)
// * CSV Reports (CSV Reader, Writer)
// * Easy Database Creator
// * File Search Library
// * Installerific (create Windows, Chocolatey, and Portable App installers)
// * Knight Data Access Layer (ORM, LINQ Provider, Generator)
// * Name Parser
// * Ninja Database Pro (Object Relational database for .NET)
// * Ninja Database Lite (Document database for .NET)
// * Ninja WinRT Database (Object database for Windows 8 Runtime, Windows Phone 8)
// * NUnit Test Generator
// * Search Databases
// * Source Code Search Tool
// * Themed Winform Wizard
// * Unused Stored Procedures
// * User Agent Parser
// * USPS Street Standardization Library
// * What's Changed? (Compare words, strings, streams, and text files)
#region Includes
using System;
using System.Collections.Generic;
using System.IO;
#if !NETSTANDARD
using System.Runtime.Serialization.Json;
#endif
#if !NETSTANDARD
using FSI.Lib.CompareNetObjects.Properties;
#endif
#endregion
#region License
//Microsoft Public License (Ms-PL)
//This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
//1. Definitions
//The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
//A "contribution" is the original software, or any additions or changes to the software.
//A "contributor" is any person that distributes its contribution under this license.
//"Licensed patents" are a contributor's patent claims that read directly on its contribution.
//2. Grant of Rights
//(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
//(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
//3. Conditions and Limitations
//(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
//(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
//(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
//(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
//(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
#endregion
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Class that allows comparison of two objects of the same type to each other. Supports classes, lists, arrays, dictionaries, child comparison and more.
/// </summary>
/// <example>
/// CompareLogic compareLogic = new CompareLogic();
///
/// Person person1 = new Person();
/// person1.DateCreated = DateTime.Now;
/// person1.Name = "Greg";
///
/// Person person2 = new Person();
/// person2.Name = "John";
/// person2.DateCreated = person1.DateCreated;
///
/// ComparisonResult result = compareLogic.Compare(person1, person2);
///
/// if (!result.AreEqual)
/// Console.WriteLine(result.DifferencesString);
///
/// </example>
public class CompareLogic : ICompareLogic
{
#region Class Variables
private ComparisonConfig _config;
#endregion
#region Properties
/// <summary>
/// The default configuration
/// </summary>
public ComparisonConfig Config
{
get => _config;
set
{
var verifyConfig = new VerifyConfig();
verifyConfig.Verify(value);
_config = value;
}
}
#endregion
#region Constructor
/// <summary>
/// Set up defaults for the comparison
/// </summary>
public CompareLogic()
{
Config = new ComparisonConfig();
}
/// <summary>
/// Pass in the configuration
/// </summary>
/// <param name="config"></param>
public CompareLogic(ComparisonConfig config)
{
Config = config;
}
#if !NETSTANDARD
/// <summary>
/// Set up defaults for the comparison
/// </summary>
/// <param name="useAppConfigSettings">If true, use settings from the app.config</param>
public CompareLogic(bool useAppConfigSettings)
{
Config = new ComparisonConfig();
if (useAppConfigSettings)
SetupWithAppConfigSettings();
}
private void SetupWithAppConfigSettings()
{
Config.MembersToIgnore = Settings.Default.MembersToIgnore == null
? new List<string>()
: new List<string>((IEnumerable<string>)Settings.Default.MembersToIgnore);
if (Settings.Default.MembersToIgnore != null)
{
foreach (var member in Settings.Default.MembersToIgnore)
{
Config.MembersToIgnore.Add(member);
}
}
Config.CompareStaticFields = Settings.Default.CompareStaticFields;
Config.CompareStaticProperties = Settings.Default.CompareStaticProperties;
Config.ComparePrivateProperties = Settings.Default.ComparePrivateProperties;
Config.ComparePrivateFields = Settings.Default.ComparePrivateFields;
Config.CompareChildren = Settings.Default.CompareChildren;
Config.CompareReadOnly = Settings.Default.CompareReadOnly;
Config.CompareFields = Settings.Default.CompareFields;
Config.IgnoreCollectionOrder = Settings.Default.IgnoreCollectionOrder;
Config.CompareProperties = Settings.Default.CompareProperties;
Config.Caching = Settings.Default.Caching;
Config.AutoClearCache = Settings.Default.AutoClearCache;
Config.MaxDifferences = Settings.Default.MaxDifferences;
Config.IgnoreUnknownObjectTypes = Settings.Default.IgnoreUnknownObjectTypes;
Config.IgnoreObjectDisposedException = Settings.Default.IgnoreObjectDisposedException;
}
#endif
#endregion
#region Public Methods
/// <summary>
/// Compare two objects of the same type to each other.
/// </summary>
/// <remarks>
/// Check the Differences or DifferencesString Properties for the differences.
/// Default MaxDifferences is 1 for performance
/// </remarks>
/// <param name="expectedObject">The expected object value to compare</param>
/// <param name="actualObject">The actual object value to compare</param>
/// <returns>True if they are equal</returns>
public ComparisonResult Compare(object expectedObject, object actualObject)
{
ComparisonResult result = new ComparisonResult(Config);
result.Watch.Start();
Config.PopulateHashSets();
RootComparer rootComparer = RootComparerFactory.GetRootComparer();
CompareParms parms = new CompareParms
{
Config = Config,
Result = result,
Object1 = expectedObject,
Object2 = actualObject,
BreadCrumb = string.Empty
};
rootComparer.Compare(parms);
if (Config.AutoClearCache)
ClearCache();
result.Watch.Stop();
return result;
}
/// <summary>
/// Reflection properties and fields are cached. By default this cache is cleared automatically after each compare.
/// </summary>
public void ClearCache()
{
Cache.ClearCache();
}
#if !NETSTANDARD
/// <summary>
/// Save the current configuration to the passed stream
/// </summary>
/// <param name="stream"></param>
public void SaveConfiguration(Stream stream)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ComparisonConfig));
ser.WriteObject(stream, Config);
if (stream.CanSeek && stream.Position > 0)
stream.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Load the current configuration from a json stream
/// </summary>
/// <param name="stream"></param>
public void LoadConfiguration(Stream stream)
{
if (stream.CanSeek && stream.Position > 0)
stream.Seek(0, SeekOrigin.Begin);
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ComparisonConfig));
Config = (ComparisonConfig)ser.ReadObject(stream);
}
#endif
#if !NETSTANDARD
/// <summary>
/// Load the current configuration from a json stream
/// </summary>
/// <param name="filePath"></param>
public void LoadConfiguration(string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Open))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ComparisonConfig));
Config = (ComparisonConfig)ser.ReadObject(stream);
Config.DifferenceCallback = d => { };
}
}
/// <summary>
/// Save the current configuration to a json file
/// </summary>
/// <param name="filePath"></param>
public void SaveConfiguration(string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Create))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ComparisonConfig));
ser.WriteObject(stream, Config);
}
}
#endif
#endregion
}
}

View File

@@ -1,358 +0,0 @@
//Provided for backward compatibility from 1.7.4
#region Includes
using System;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Obsolete Use CompareLogic instead
/// </summary>
[Obsolete("Use CompareLogic instead", true)]
public class CompareObjects
{
#region Class Variables
private readonly CompareLogic _logic;
private ComparisonResult _result;
#endregion
#region Constructor
/// <summary>
/// Obsolete Use CompareLogic instead
/// </summary>
[Obsolete("Use CompareLogic instead", true)]
public CompareObjects()
{
_logic = new CompareLogic();
_result = new ComparisonResult(_logic.Config);
}
#if !NETSTANDARD
/// <summary>
/// Obsolete Use CompareLogic instead
/// </summary>
[Obsolete("Use CompareLogic instead", true)]
public CompareObjects(bool useAppConfigSettings)
{
_logic = new CompareLogic(useAppConfigSettings);
_result = new ComparisonResult(_logic.Config);
}
#endif
#endregion
#region Properties
#if !NETSTANDARD
/// <summary>
/// Obsolete Use the ComparisonResult.ElapsedMilliseconds returned from CompareLogic.Compare
/// </summary>
[Obsolete("Use the ComparisonResult.ElapsedMilliseconds returned from CompareLogic.Compare", true)]
public long ElapsedMilliseconds
{
get { return _result.ElapsedMilliseconds; }
}
#endif
/// <summary>
/// Obsolete Use CompareLogic.Config.ShowBreadcrumb instead
/// </summary>
[Obsolete("Use CompareLogic.Config.ShowBreadcrumb instead", true)]
public bool ShowBreadcrumb
{
get { return _logic.Config.ShowBreadcrumb; }
set { _logic.Config.ShowBreadcrumb = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.MembersToIgnore for members or CompareLogic.Config.ClassTypesToIgnore instead
/// </summary>
[Obsolete("Use CompareLogic.Config.MembersToIgnore for members or CompareLogic.Config.ClassTypesToIgnore instead", true)]
public List<string> ElementsToIgnore
{
get { return _logic.Config.MembersToIgnore.ToList(); }
set
{
_logic.Config.MembersToIgnore.Clear();
foreach (var item in value)
{
_logic.Config.MembersToIgnore.Add(item);
}
}
}
/// <summary>
/// Obsolete Use CompareLogic.Config.MembersToInclude or CompareLogic.Config.ClassTypesToInclude instead
/// </summary>
[Obsolete("Use CompareLogic.Config.MembersToInclude or CompareLogic.Config.ClassTypesToInclude instead", true)]
public List<string> ElementsToInclude
{
get { return _logic.Config.MembersToInclude.ToList(); }
set
{
_logic.Config.MembersToInclude.Clear();
foreach (var item in value)
{
_logic.Config.MembersToInclude.Add(item);
}
}
}
//Security restriction in Silverlight prevents getting private properties and fields
#if !NETSTANDARD
/// <summary>
/// Obsolete Use CompareLogic.Config.ComparePrivateProperties instead
/// </summary>
[Obsolete("Use CompareLogic.Config.ComparePrivateProperties instead", true)]
public bool ComparePrivateProperties
{
get { return _logic.Config.ComparePrivateProperties; }
set { _logic.Config.ComparePrivateProperties = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.ComparePrivateFields instead
/// </summary>
[Obsolete("Use CompareLogic.Config.ComparePrivateFields instead", true)]
public bool ComparePrivateFields
{
get { return _logic.Config.ComparePrivateFields; }
set { _logic.Config.ComparePrivateFields = value; }
}
#endif
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareStaticProperties instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareStaticProperties instead", true)]
public bool CompareStaticProperties
{
get { return _logic.Config.CompareStaticProperties; }
set { _logic.Config.CompareStaticProperties = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareStaticFields instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareStaticFields instead", true)]
public bool CompareStaticFields
{
get { return _logic.Config.CompareStaticFields; }
set { _logic.Config.CompareStaticFields = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareChildren instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareChildren instead", true)]
public bool CompareChildren
{
get { return _logic.Config.CompareChildren; }
set { _logic.Config.CompareChildren = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareReadOnly instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareReadOnly instead", true)]
public bool CompareReadOnly
{
get { return _logic.Config.CompareReadOnly; }
set { _logic.Config.CompareReadOnly = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareFields instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareFields instead", true)]
public bool CompareFields
{
get { return _logic.Config.CompareFields; }
set { _logic.Config.CompareFields = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.IgnoreCollectionOrder instead
/// </summary>
[Obsolete("Use CompareLogic.Config.IgnoreCollectionOrder instead", true)]
public bool IgnoreCollectionOrder
{
get { return _logic.Config.IgnoreCollectionOrder; }
set { _logic.Config.IgnoreCollectionOrder = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CompareProperties instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CompareProperties instead", true)]
public bool CompareProperties
{
get { return _logic.Config.CompareProperties; }
set { _logic.Config.CompareProperties = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.MaxDifferences instead
/// </summary>
[Obsolete("Use CompareLogic.Config.MaxDifferences instead", true)]
public int MaxDifferences
{
get { return _logic.Config.MaxDifferences; }
set { _logic.Config.MaxDifferences = value; }
}
/// <summary>
/// Obsolete Use the ComparisonResult.Differences returned from CompareLogic.Compare
/// </summary>
[Obsolete("Use the ComparisonResult.Differences returned from CompareLogic.Compare", true)]
public List<Difference> Differences
{
get { return _result.Differences; }
set { _result.Differences = value; }
}
/// <summary>
/// Obsolete Use the ComparisonResult.DifferencesString returned from CompareLogic.Compare
/// </summary>
[Obsolete("Use the ComparisonResult.DifferencesString returned from CompareLogic.Compare", true)]
public string DifferencesString
{
get { return _result.DifferencesString; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.AutoClearCache instead
/// </summary>
[Obsolete("Use CompareLogic.Config.AutoClearCache instead", true)]
public bool AutoClearCache
{
get { return _logic.Config.AutoClearCache; }
set { _logic.Config.AutoClearCache = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.Caching instead
/// </summary>
[Obsolete("Use CompareLogic.Config.Caching instead", true)]
public bool Caching
{
get { return _logic.Config.Caching; }
set { _logic.Config.Caching = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.AttributesToIgnore instead
/// </summary>
[Obsolete("Use CompareLogic.Config.AttributesToIgnore instead", true)]
public List<Type> AttributesToIgnore
{
get { return _logic.Config.AttributesToIgnore.ToList(); }
set
{
_logic.Config.AttributesToIgnore.Clear();
foreach (var item in value)
{
_logic.Config.AttributesToIgnore.Add(item);
}
}
}
/// <summary>
/// Obsolete Use CompareLogic.Config.IgnoreObjectTypes instead
/// </summary>
[Obsolete("Use CompareLogic.Config.IgnoreObjectTypes instead", true)]
public bool IgnoreObjectTypes
{
get { return _logic.Config.IgnoreObjectTypes; }
set { _logic.Config.IgnoreObjectTypes = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CustomComparers instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CustomComparers", true)]
public Func<Type, bool> IsUseCustomTypeComparer { get; set; }
/// <summary>
/// Obsolete Use CompareLogic.Config.CustomComparers instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CustomComparers", true)]
public Action<CompareObjects, object, object, string> CustomComparer { get; set; }
/// <summary>
/// Obsolete Use CompareLogic.Config.ExpectedName instead
/// </summary>
[Obsolete("Use CompareLogic.Config.ExpectedName instead", true)]
public string ExpectedName
{
get { return _logic.Config.ExpectedName; }
set { _logic.Config.ExpectedName = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.ActualName instead
/// </summary>
[Obsolete("Use CompareLogic.Config.ActualName instead", true)]
public string ActualName
{
get { return _logic.Config.ActualName; }
set { _logic.Config.ActualName = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.DifferenceCallback instead
/// </summary>
[Obsolete("Use CompareLogic.Config.DifferenceCallback instead", true)]
public Action<Difference> DifferenceCallback
{
get { return _logic.Config.DifferenceCallback; }
set { _logic.Config.DifferenceCallback = value; }
}
/// <summary>
/// Obsolete Use CompareLogic.Config.CollectionMatchingSpec instead
/// </summary>
[Obsolete("Use CompareLogic.Config.CollectionMatchingSpec instead", true)]
public Dictionary<Type, IEnumerable<string>> CollectionMatchingSpec
{
get { return _logic.Config.CollectionMatchingSpec; }
set { _logic.Config.CollectionMatchingSpec = value; }
}
#endregion
#region Methods
/// <summary>
/// Obsolete Use CompareLogic.Compare instead
/// </summary>
[Obsolete("Use CompareLogic.Compare instead", true)]
public bool Compare(object object1, object object2)
{
_result = _logic.Compare(object1, object2);
return _result.AreEqual;
}
/// <summary>
/// Obsolete Use CompareLogic.ClearCache instead
/// </summary>
[Obsolete("Use CompareLogic.ClearCache instead", true)]
public void ClearCache()
{
_logic.ClearCache();
}
#endregion
}
}

View File

@@ -1,71 +0,0 @@
using System;
using FSI.Lib.CompareNetObjects.TypeComparers;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Compare Parameters
/// </summary>
public class CompareParms
{
/// <summary>
/// The configuration settings
/// </summary>
public ComparisonConfig Config { get; set; }
/// <summary>
/// The type of the first object
/// </summary>
public Type Object1Type { get; set; }
/// <summary>
/// The type of the second object
/// </summary>
public Type Object2Type { get; set; }
/// <summary>
/// The declared type of the first object in its parent. e.g. IList<T>
/// </summary>
public Type Object1DeclaredType { get; set; }
/// <summary>
/// The declared type of the second object in its parent. e.g. IList<T>
/// </summary>
public Type Object2DeclaredType { get; set; }
/// <summary>
/// Details about the comparison
/// </summary>
public ComparisonResult Result { get; set; }
/// <summary>
/// A reference to the parent object1
/// </summary>
public object ParentObject1 { get; set; }
/// <summary>
/// A reference to the parent object2
/// </summary>
public object ParentObject2 { get; set; }
/// <summary>
/// The first object to be compared
/// </summary>
public object Object1 { get; set; }
/// <summary>
/// The second object to be compared
/// </summary>
public object Object2 { get; set; }
/// <summary>
/// The breadcrumb in the tree
/// </summary>
public string BreadCrumb { get; set; }
/// <summary>
/// Custom comparer used to assert <para>Object1</para>
/// </summary>
public BaseTypeComparer CustomPropertyComparer { get; set; }
}
}

View File

@@ -1,650 +0,0 @@
using System;
using System.Collections.Generic;
using FSI.Lib.CompareNetObjects.TypeComparers;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
#if !NETSTANDARD
using System.Runtime.Serialization;
#endif
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Configuration
/// </summary>
#if !NETSTANDARD
[DataContract]
#endif
public class ComparisonConfig
{
#region Class Variables
private Action<Difference> _differenceCallback;
private int _maxStructDepth;
#endregion
#region Constructors
/// <summary>
/// Default Constructor
/// </summary>
public ComparisonConfig()
{
Reset();
}
#endregion
#region Properties
internal HashSet<Type> AttributesToIgnoreSet { get; set; }
internal HashSet<string> MembersToIgnoreSet { get; set; }
internal HashSet<string> MembersToIncludeSet { get; set; }
internal HashSet<Type> ClassTypesToIgnoreSet { get; set; }
internal HashSet<Type> ClassTypesToIncludeSet { get; set; }
internal HashSet<Type> TypesToIgnoreSet { get; set; }
internal HashSet<Type> TypesToIncludeSet { get; set; }
internal HashSet<Type> RequiredAttributesToCompareSet { get; set; }
/// <summary>
/// By default Compare .NET Objects uses reference equal to identify objects.
/// Versions 4.61 and older used the hash code. Setting this to true will identify objects by hash code instead of reference equals.
/// The default is false
/// </summary>
public bool UseHashCodeIdentifier { get; set; }
/// <summary>
/// When comparing strings or StringBuilder types, perform a case sensitive comparison. The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CaseSensitive { get; set; }
/// <summary>
/// Ignore exceptions when objects are disposed
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreObjectDisposedException { get; set; }
/// <summary>
/// Ignore millisecond differences between DateTime values or DateTimeOffset values. The default is 0 (any time difference will be shown).
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public int MaxMillisecondsDateDifference { get; set; }
/// <summary>
/// When comparing DateTimeOffsets, offsets will be compared as well as the UtcDateTimes. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareDateTimeOffsetWithOffsets { get; set; }
/// <summary>
/// When comparing DateTimeOffsets, timezone difference will be ignored by changing both object to their UTC equivalent value. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreDateTimeOffsetTimezones { get; set; }
/// <summary>
/// When comparing struct, the depth to compare for children. The default is 2, the max is 5
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public int MaxStructDepth
{
get { return _maxStructDepth; }
set
{
if (value < 1 || value > 5)
{
throw new ArgumentOutOfRangeException("MaxStructDepth", "Cannot be less than 1 or greater than 5");
}
_maxStructDepth = value;
}
}
/// <summary>
/// If true, unknown object types will be ignored instead of throwing an exception. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreUnknownObjectTypes { get; set; }
/// <summary>
/// If true, invalid indexers will be skipped. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool SkipInvalidIndexers { get; set; }
/// <summary>
/// If a class implements an interface then only members of the interface will be compared. The default is all members are compared.
/// </summary>
public List<Type> InterfaceMembers { get; set; }
#if !NETSTANDARD
[DataMember(Name = "InterfaceMembers")]
private List<string> InterfaceMembersSerializer
{
get { return TypeHelper.ListOfTypesSerializer(InterfaceMembers);}
set { InterfaceMembers = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// Show breadcrumb at each stage of the comparision. The default is false.
/// This is useful for debugging deep object graphs.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool ShowBreadcrumb { get; set; }
/// <summary>
/// A list of class types to be ignored in the comparison. The default is to compare all class types.
/// </summary>
public List<Type> ClassTypesToIgnore { get; set; }
#if !NETSTANDARD
[DataMember(Name = "ClassTypesToIgnore")]
private List<string> ClassTypesToIgnoreSerializer
{
get { return TypeHelper.ListOfTypesSerializer(ClassTypesToIgnore); }
set { ClassTypesToIgnore = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// Only these class types will be compared. The default is to compare all class types.
/// </summary>
/// <remarks>If you specify a class type here no other class types will be compared unless it is in this list.</remarks>
public List<Type> ClassTypesToInclude { get; set; }
#if !NETSTANDARD
[DataMember(Name = "ClassTypesToInclude")]
private List<string> ClassTypesToIncludeSerializer
{
get { return TypeHelper.ListOfTypesSerializer(ClassTypesToInclude); }
set { ClassTypesToInclude = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// A list of types to be ignored in the comparison. The default is to compare all types. A typical thing to not compare are GUIDs
/// </summary>
public List<Type> TypesToIgnore { get; set; }
#if !NETSTANDARD
[DataMember(Name = "TypesToIgnore")]
private List<string> TypesToIgnoreSerializer
{
get { return TypeHelper.ListOfTypesSerializer(TypesToIgnore); }
set { TypesToIgnore = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// Only these types will be compared. The default is to compare all types.
/// </summary>
/// <remarks>If you specify a type here no others will be compared unless it is in this list. You must specify ALL Types that you want to compare.</remarks>
public List<Type> TypesToInclude { get; set; }
#if !NETSTANDARD
[DataMember(Name = "TypesToInclude")]
private List<string> TypesToIncludeSerializer
{
get { return TypeHelper.ListOfTypesSerializer(TypesToInclude); }
set { TypesToInclude = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// Ignore Data Table Names, Data Table Column Names, properties, or fields by name during the comparison. Case sensitive. The default is to compare all members.
/// </summary>
/// <example>MembersToIgnore.Add("CreditCardNumber");
/// MembersToIgnore.Add("Invoice.InvoiceGuid");
/// MembersToIgnore.Add("*Id");
/// </example>
#if !NETSTANDARD
[DataMember]
#endif
public List<string> MembersToIgnore { get ; set; }
/// <summary>
/// Ignore property during the comparison. Property is specific to the generic type.
/// </summary>
/// <param name="ignoredProperty"></param>
/// <typeparam name="TClass"></typeparam>
/// <exception cref="ArgumentException"></exception>
/// <example>IgnoreProperty&lt;Person&gt;(x => x.Name)</example>
public void IgnoreProperty<TClass>(Expression<Func<TClass, object>> ignoredProperty)
{
LambdaExpression lambda = ignoredProperty;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression unaryExpression)
{
memberExpression = unaryExpression.Operand as MemberExpression ??
// catches methods, maybe other things
throw new ArgumentException(
$"IgnoreProperty can only be used with properties. {ignoredProperty} is not a property.");
}
else
{
memberExpression = (MemberExpression) lambda.Body;
}
var propInfo = memberExpression.Member as PropertyInfo;
if (propInfo == null)
// catches fields, maybe other things
{
throw new ArgumentException($"IgnoreProperty can only be used with properties. {ignoredProperty} is not a property.");
}
var name = propInfo.Name;
MembersToIgnore.Add(typeof(TClass).Name + "." + name);
}
/// <summary>
/// Define a Custom Property Comparer using a lambda expression
/// </summary>
/// <typeparam name="TClass"></typeparam>
/// <param name="customProperty"></param>
/// <param name="validator"></param>
public void CustomPropertyComparer<TClass>(Expression<Func<TClass, object>> customProperty, BaseTypeComparer validator)
{
LambdaExpression lambda = customProperty;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression unaryExpression)
{
memberExpression = unaryExpression.Operand as MemberExpression ??
// catches methods, maybe other things
throw new ArgumentException(
$"Custom property comparer can only be used with properties. {customProperty} is not a property.");
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
var propInfo = memberExpression.Member as PropertyInfo;
if (propInfo == null)
// catches fields, maybe other things
{
throw new ArgumentException($"Custom property comparer can only be used with properties. {customProperty} is not a property.");
}
var name = propInfo.Name;
CustomPropertyComparers.Add(typeof(TClass).Name + "." + name, validator);
}
/// <summary>
/// Only compare elements by name for Data Table Names, Data Table Column Names, properties and fields. Case sensitive. The default is to compare all members.
/// </summary>
/// <example>MembersToInclude.Add("FirstName")</example>
#if !NETSTANDARD
[DataMember]
#endif
public List<string> MembersToInclude { get; set; }
#if !NETSTANDARD1_3
/// <summary>
/// If true, private properties and fields will be compared. The default is false. Silverlight and WinRT restricts access to private variables.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool ComparePrivateProperties { get; set; }
#endif
#if !NETSTANDARD1_3
/// <summary>
/// If true, private fields will be compared. The default is false. Silverlight and WinRT restricts access to private variables.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool ComparePrivateFields { get; set; }
#endif
/// <summary>
/// If true, static properties will be compared. The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareStaticProperties { get; set; }
/// <summary>
/// If true, static fields will be compared. The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareStaticFields { get; set; }
/// <summary>
/// If true, child objects will be compared. The default is true.
/// If false, and a list or array is compared list items will be compared but not their children.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareChildren { get; set; }
/// <summary>
/// If true, compare read only properties (only the getter is implemented). The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareReadOnly { get; set; }
/// <summary>
/// If true, compare fields of a class (see also CompareProperties). The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareFields { get; set; }
/// <summary>
/// If true, compare each item within a collection to every item in the other. The default is false. WARNING: setting this to true significantly impacts performance.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreCollectionOrder { get; set; }
/// <summary>
/// If true, compare properties of a class (see also CompareFields). The default is true.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareProperties { get; set; }
/// <summary>
/// The maximum number of differences to detect. The default is 1 for performance reasons.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public int MaxDifferences { get; set; }
/// <summary>
/// The maximum number of differences to detect when comparing byte arrays. The default is 1.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public int MaxByteArrayDifferences { get; set; }
/// <summary>
/// Reflection properties and fields are cached. By default this cache is cleared after each compare. Set to false to keep the cache for multiple compares.
/// </summary>
/// <seealso cref="Caching"/>
#if !NETSTANDARD
[DataMember]
#endif
public bool AutoClearCache { get; set; }
/// <summary>
/// By default properties and fields for types are cached for each compare. By default this cache is cleared after each compare.
/// </summary>
/// <seealso cref="AutoClearCache"/>
#if !NETSTANDARD
[DataMember]
#endif
public bool Caching { get; set; }
/// <summary>
/// A list of attributes to ignore a class, property or field
/// </summary>
/// <example>AttributesToIgnore.Add(typeof(XmlIgnoreAttribute));</example>
public List<Type> AttributesToIgnore { get; set; }
#if !NETSTANDARD
[DataMember(Name = "AttributesToIgnore")]
private List<string> AttributesToIgnoreSerializer
{
get { return TypeHelper.ListOfTypesSerializer(AttributesToIgnore); }
set { AttributesToIgnore = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// If a property or field don't have at least one of the attributes in this list, it will be ignored
/// </summary>
/// <example>RequiredAttributesToCompare.Add(typeof(XmlIgnoreAttribute));</example>
public List<Type> RequiredAttributesToCompare { get; set; }
#if !NETSTANDARD
[DataMember(Name = "RequiredAttributesToCompare")]
private List<string> RequiredAttributesToCompareSerializer
{
get { return TypeHelper.ListOfTypesSerializer(RequiredAttributesToCompare); }
set { RequiredAttributesToCompare = TypeHelper.ListOfTypesDeserializer(value); }
}
#endif
/// <summary>
/// If true, objects will be compared ignore their type diferences. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreObjectTypes { get; set; }
/// <summary>
/// In the differences string, this is the name for expected name. The default is: Expected
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public string ExpectedName { get; set; }
/// <summary>
/// In the differences string, this is the name for the actual name. The default is: Actual
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public string ActualName { get; set; }
/// <summary>
/// Callback invoked each time the comparer finds a difference. The default is no call back.
/// </summary>
public Action<Difference> DifferenceCallback
{
get { return _differenceCallback; }
set
{
if (null != value)
{
_differenceCallback = value;
}
}
}
/// <summary>
/// This property is used when IgnoreCollectionOrder is set to true, otherwise it has no effect.
/// Sometimes one wants to match items between collections by some key first, and then
/// compare the matched objects. Without this, the comparer basically says there is no
/// match in collection B for any given item in collection A that doesn't Compare with a result of true.
/// The results of this aren't particularly useful for object graphs that are mostly the same, but not quite.
/// Enter CollectionMatchingSpec
///
/// The enumerable strings should be property (not field, for now, to keep it simple) names of the
/// Type when encountered that will be used for matching
///
/// You can use complex type properties, too, as part of the key to match. To match on all props/fields on
/// such a matching key, Don't set this property (default comparer behavior)
/// NOTE: types are looked up as exact. e.g. if foo is an entry in the dictionary and bar is a
/// sub-class of foo, upon encountering a bar type, the comparer will not find the entry of foo
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public Dictionary<Type, IEnumerable<string>> CollectionMatchingSpec { get; set; }
/// <summary>
/// A list of custom comparers that take priority over the built in comparers
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public List<BaseTypeComparer> CustomComparers { get; set; }
/// <summary>
/// A list of custom property comparers that take priority over the built in and type comparers
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public Dictionary<string, BaseTypeComparer> CustomPropertyComparers { get; set; }
/// <summary>
/// If true, string.empty and null will be treated as equal for Strings and String Builder. The default is false.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool TreatStringEmptyAndNullTheSame { get; set; }
/// <summary>
/// If true, leading and trailing whitespaces will be ignored for Strings and String Builder. The default is false.
/// </summary>
#if !DNCORE
[DataMember]
#endif
public bool IgnoreStringLeadingTrailingWhitespace { get; set; }
/// <summary>
/// The precision to compare double values. The default is 0.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public double DoublePrecision { get; set; }
/// <summary>
/// The precision to compare decimal values. The default is 0.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public decimal DecimalPrecision { get; set; }
#if !NETSTANDARD
[DataMember]
#endif
public bool IgnoreConcreteTypes { get; set; }
/// <summary>
/// If true, properties that are defined in the actual object but missing in the expected object will not be flagged as differences. Default is true.
/// </summary>
public bool IgnoreMissingProperties { get; set; }
/// <summary>
/// If true, fields that are defined in the actual object but missing in the expected object will not be flagged as differences. Default is true.
/// </summary>
public bool IgnoreMissingFields { get; set; }
#endregion
#region Methods
//These hash sets are used for performance
internal void PopulateHashSets()
{
AttributesToIgnoreSet = new HashSet<Type>((AttributesToIgnore ?? new List<Type>()).Distinct());
MembersToIgnoreSet = new HashSet<String>((MembersToIgnore ?? new List<String>()).Distinct());
MembersToIncludeSet = new HashSet<String>((MembersToInclude ?? new List<String>()).Distinct());
ClassTypesToIgnoreSet = new HashSet<Type>((ClassTypesToIgnore ?? new List<Type>()).Distinct());
ClassTypesToIncludeSet = new HashSet<Type>((ClassTypesToInclude ?? new List<Type>()).Distinct());
TypesToIgnoreSet = new HashSet<Type>((TypesToIgnore ?? new List<Type>()).Distinct());
TypesToIncludeSet = new HashSet<Type>((TypesToInclude ?? new List<Type>()).Distinct());
RequiredAttributesToCompareSet = new HashSet<Type>((RequiredAttributesToCompare ?? new List<Type>()).Distinct());
}
/// <summary>
/// Backing member that supports <see cref="HasWildcardMembersToExclude"/>
/// </summary>
private bool? _hasWildcardInMembersToIgnore;
/// <summary>
/// Computed value of whether or not exclusion list has wildcards.
/// </summary>
public bool HasWildcardMembersToExclude()
{
if (_hasWildcardInMembersToIgnore.HasValue)
{
return _hasWildcardInMembersToIgnore.Value;
}
_hasWildcardInMembersToIgnore = MembersToIgnoreSet.Any(x => x.IndexOf("*") > -1);
return _hasWildcardInMembersToIgnore.Value;
}
/// <summary>
/// Reset the configuration to the default values
/// </summary>
public void Reset()
{
AttributesToIgnore = new List<Type>();
RequiredAttributesToCompare = new List<Type>();
_differenceCallback = d => { };
MembersToIgnore = new List<string>();
_hasWildcardInMembersToIgnore = null;
MembersToInclude = new List<string>();
ClassTypesToIgnore = new List<Type>();
ClassTypesToInclude = new List<Type>();
TypesToIgnore = new List<Type>();
TypesToInclude = new List<Type>();
CompareStaticFields = true;
CompareStaticProperties = true;
#if !NETSTANDARD1_3
ComparePrivateProperties = false;
ComparePrivateFields = false;
#endif
CustomPropertyComparers = new Dictionary<string, BaseTypeComparer>();
CompareChildren = true;
CompareReadOnly = true;
CompareFields = true;
CompareDateTimeOffsetWithOffsets = false;
IgnoreCollectionOrder = false;
CompareProperties = true;
Caching = true;
AutoClearCache = true;
IgnoreObjectTypes = false;
MaxDifferences = 1;
ExpectedName = "Expected";
ActualName = "Actual";
CustomComparers = new List<BaseTypeComparer>();
TreatStringEmptyAndNullTheSame = false;
InterfaceMembers = new List<Type>();
SkipInvalidIndexers = false;
MaxByteArrayDifferences = 1;
CollectionMatchingSpec = new Dictionary<Type, IEnumerable<string>>();
IgnoreUnknownObjectTypes = false;
MaxStructDepth = 2;
CaseSensitive = true;
IgnoreStringLeadingTrailingWhitespace = false;
IgnoreMissingProperties = true;
IgnoreMissingFields = true;
}
#endregion
}
}

View File

@@ -1,245 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Details about the comparison
/// </summary>
public class ComparisonResult
{
#region Class Variables
private string _differencesString;
/// <summary>
/// Keep track of parent objects in the object hierarchy by using reference equals
/// </summary>
private readonly Dictionary<object, int> _referenceParents = new Dictionary<object, int>();
private readonly Dictionary<int, int> _hashParents = new Dictionary<int, int>();
#endregion
#region Constructors
/// <summary>
/// Set the configuration for the comparison
/// </summary>
/// <param name="config"></param>
public ComparisonResult(ComparisonConfig config)
{
Config = config;
Differences = new List<Difference>();
Watch = new Stopwatch();
}
#endregion
#region Properties
/// <summary>
/// Keep track of the depth of structs being compared. Used with ComparisonConfig.MaxStructDepth
/// </summary>
internal int CurrentStructDepth { get; set; }
/// <summary>
/// Configuration
/// </summary>
public ComparisonConfig Config { get; private set; }
/// <summary>
/// Used to time how long the comparison took
/// </summary>
internal Stopwatch Watch { get; set; }
/// <summary>
/// The amount of time in milliseconds it took for the comparison
/// </summary>
public long ElapsedMilliseconds
{
get { return Watch.ElapsedMilliseconds; }
}
/// <summary>
/// The differences found during the compare
/// </summary>
public List<Difference> Differences { get; set; }
/// <summary>
/// The differences found in a string suitable for a textbox
/// </summary>
public string DifferencesString
{
get
{
if (String.IsNullOrEmpty(_differencesString))
{
StringBuilder sb = new StringBuilder(4096);
sb.AppendLine();
sb.AppendFormat("Begin Differences ({0} differences):{1}", Differences.Count, Environment.NewLine);
foreach (Difference item in Differences)
{
sb.AppendLine(item.ToString());
}
sb.AppendFormat("End Differences (Maximum of {0} differences shown).", Config.MaxDifferences);
_differencesString = sb.ToString();
}
return _differencesString;
}
set { _differencesString = value; }
}
/// <summary>
/// Returns true if the objects are equal
/// </summary>
public bool AreEqual
{
get { return Differences.Count == 0; }
}
/// <summary>
/// Returns true if the number of differences has reached the maximum
/// </summary>
public bool ExceededDifferences
{
get { return Differences.Count >= Config.MaxDifferences; }
}
#endregion
#region Methods
/// <summary>
/// Add parent, handle references count
/// </summary>
/// <param name="objectReference"></param>
public void AddParent(object objectReference)
{
if (objectReference == null)
return;
if (Config.UseHashCodeIdentifier && _hashParents == null)
return;
if (!Config.UseHashCodeIdentifier && _referenceParents == null)
return;
if (Config.UseHashCodeIdentifier)
{
int hash = objectReference.GetHashCode();
if (hash == 0)
return;
if (!_hashParents.ContainsKey(hash))
{
_hashParents.Add(hash, 1);
}
else
{
_hashParents[hash]++;
}
}
else
{
if (!_referenceParents.ContainsKey(objectReference))
{
_referenceParents.Add(objectReference, 0);
}
else
{
_referenceParents[objectReference]++;
}
}
}
/// <summary>
/// Remove parent, handle references count
/// </summary>
/// <param name="objectReference"></param>
public void RemoveParent(object objectReference)
{
if (objectReference == null)
return;
if (Config.UseHashCodeIdentifier && _hashParents == null)
return;
if (!Config.UseHashCodeIdentifier && _referenceParents == null)
return;
try
{
if (Config.UseHashCodeIdentifier)
{
int hash = objectReference.GetHashCode();
if (_hashParents.ContainsKey(hash))
{
if (_hashParents[hash] <= 1)
{
_hashParents.Remove(hash);
}
else
{
_hashParents[hash]--;
}
}
}
else
{
if (_referenceParents.ContainsKey(objectReference))
{
if (_referenceParents[objectReference] <= 1)
{
_referenceParents.Remove(objectReference);
}
else
{
_referenceParents[objectReference]--;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
/// <summary>
/// Returns true if we have encountered this parent before
/// </summary>
/// <param name="objectReference"></param>
/// <returns></returns>
protected internal bool IsParent(object objectReference)
{
if (objectReference == null)
return false;
if (Config.UseHashCodeIdentifier && _hashParents == null)
return false;
if (!Config.UseHashCodeIdentifier && _referenceParents == null)
return false;
if (Config.UseHashCodeIdentifier)
{
int hash = objectReference.GetHashCode();
if (hash == 0)
return false;
return _hashParents.ContainsKey(hash);
}
else
{
return _referenceParents.ContainsKey(objectReference);
}
}
#endregion
}
}

View File

@@ -1,85 +0,0 @@
using System;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
using FSI.Lib.CompareNetObjects.TypeComparers;
/// <summary>
/// Get custom validator based on property
/// </summary>
public static class CustomValidationLogic
{
/// <summary>
/// Get validator for a member of an expando object
/// </summary>
/// <param name="config"></param>
/// <param name="name"></param>
/// <param name="type"></param>
/// <returns></returns>
public static BaseTypeComparer CustomValidatorForDynamicMember(ComparisonConfig config, string name, Type type)
{
BaseTypeComparer customValidator = null;
if (config.CustomPropertyComparers.Count == 0)
{
return customValidator;
}
//Only compare specific member names
customValidator = GetValidatorByName(config, name);
if (customValidator != null)
return customValidator;
//Get by type.membername
customValidator = GetValidatorByName(config, type.Name + "." + name);
if (customValidator != null)
return customValidator;
return customValidator;
}
/// <summary>
/// Get validator for a member
/// </summary>
/// <param name="config"></param>
/// <param name="info"></param>
/// <param name="objectType"></param>
/// <returns></returns>
public static BaseTypeComparer CustomValidatorForMember(ComparisonConfig config, MemberInfo info, Type objectType)
{
BaseTypeComparer customValidator = null;
if (config.CustomPropertyComparers.Count == 0)
{
return customValidator;
}
//Get by objecttype.membername
customValidator = GetValidatorByName(config, objectType.Name + "." + info.Name);
if (customValidator != null)
return customValidator;
//Get by declaringType.membername
if (info.DeclaringType != null)
{
customValidator = GetValidatorByName(config, info.DeclaringType.Name + "." + info.Name);
if (customValidator != null)
return customValidator;
}
//Get exactly by the name of the member
customValidator = GetValidatorByName(config, info.Name);
if (customValidator != null)
return customValidator;
return customValidator;
}
private static BaseTypeComparer GetValidatorByName(ComparisonConfig config, string name)
{
config.CustomPropertyComparers.TryGetValue(name, out var comparer);
return comparer;
}
}
}

View File

@@ -1,203 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Detailed information about the difference
/// </summary>
public class Difference
{
/// <summary>
/// Name of Expected Object
/// </summary>
public string ExpectedName { get; set; }
/// <summary>
/// Name of Actual Object
/// </summary>
public string ActualName { get; set; }
/// <summary>
/// Returns the parent property name
/// </summary>
public string ParentPropertyName
{
get
{
if (PropertyName.EndsWith("]") && PropertyName.Contains("["))
{
int lastLeftSquare = PropertyName.LastIndexOf('[');
return PropertyName.Substring(0, lastLeftSquare);
}
if (PropertyName.Contains("."))
{
int lastPeriod = PropertyName.LastIndexOf('.');
if (lastPeriod > 0)
return PropertyName.Substring(0, lastPeriod);
}
return string.Empty;
}
}
/// <summary>
/// The breadcrumb of the property leading up to the value
/// </summary>
public string PropertyName { get; set; }
/// <summary>
/// The child property name
/// </summary>
public string ChildPropertyName { get; set; }
/// <summary>
/// Object1 Value as a string
/// </summary>
public string Object1Value { get; set; }
/// <summary>
/// Object2 Value as a string
/// </summary>
public string Object2Value { get; set; }
/// <summary>
/// The type of the first object
/// </summary>
public string Object1TypeName { get; set; }
/// <summary>
/// The type of the second object
/// </summary>
public string Object2TypeName { get; set; }
/// <summary>
/// A reference to the parent of object1
/// </summary>
public object ParentObject1 { get; set; }
/// <summary>
/// A reference to the parent of object2
/// </summary>
public object ParentObject2 { get; set; }
/// <summary>
/// Object1 as a reference
/// </summary>
public object Object1 { get; set; }
/// <summary>
/// Object2 as a reference
/// </summary>
public object Object2 { get; set; }
/// <summary>
/// Prefix to put on the beginning of the message
/// </summary>
public string MessagePrefix { get; set; }
/// <summary>
/// Item and property name only
/// </summary>
/// <returns></returns>
public string GetShortItem()
{
string message;
if (!String.IsNullOrEmpty(PropertyName))
{
if (String.IsNullOrEmpty(ChildPropertyName))
{
message = String.Format("{0}", PropertyName);
}
else
{
message = String.Format("{0}.{1}",
PropertyName,
ChildPropertyName);
}
}
else
{
message = String.Format("{0} != {1}",
ExpectedName,
ActualName);
}
if (!String.IsNullOrEmpty(MessagePrefix))
message = String.Format("{0}: {1}", MessagePrefix, message);
message = message.Replace("..", ".");
message = message.Replace(".[", "[");
return message;
}
/// <summary>
/// The type and index of what is compared
/// </summary>
/// <returns></returns>
public string GetWhatIsCompared()
{
string message;
if (!String.IsNullOrEmpty(PropertyName))
{
if (String.IsNullOrEmpty(ChildPropertyName))
{
message = String.Format("Types [{3},{4}], Item {0}.{2} != {1}.{2}",
ExpectedName,
ActualName,
PropertyName,
Object1TypeName,
Object2TypeName);
}
else
{
message = String.Format("Types [{4},{5}], Item {0}.{2}.{3} != {1}.{2}.{3}",
ExpectedName,
ActualName,
PropertyName,
ChildPropertyName,
Object1TypeName,
Object2TypeName);
}
}
else if (!String.IsNullOrEmpty(ChildPropertyName))
{
message = String.Format("Types [{2}.{4},{3}.{4}], Item {0} != {1}",
ExpectedName,
ActualName,
Object1TypeName,
Object2TypeName,
ChildPropertyName);
}
else
{
message = String.Format("Types [{2},{3}], Item {0} != {1}",
ExpectedName,
ActualName,
Object1TypeName,
Object2TypeName);
}
if (!String.IsNullOrEmpty(MessagePrefix))
message = String.Format("{0}: {1}", MessagePrefix, message);
message = message.Replace("..", ".");
message = message.Replace(".[", "[");
return message;
}
/// <summary>
/// Nicely formatted string
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("{0}, Values ({1},{2})", GetWhatIsCompared(), Object1Value, Object2Value);
}
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.IO;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Environment helper class
/// </summary>
public static class EnvironmentHelper
{
/// <summary>
/// Returns true if we are running in Windows
/// </summary>
/// <returns></returns>
public static bool IsWindows()
{
string windir = Environment.GetEnvironmentVariable("windir");
return !string.IsNullOrEmpty(windir) && windir.Contains(@"\") && Directory.Exists(windir);
}
}
}

View File

@@ -1,220 +0,0 @@

using System;
using System.Linq;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Exclude types depending upon the configuration
/// </summary>
public static class ExcludeLogic
{
/// <summary>
/// Exclude a member of an expando object
/// </summary>
/// <param name="config"></param>
/// <param name="name"></param>
/// <param name="type"></param>
/// <returns></returns>
public static bool ShouldExcludeDynamicMember(ComparisonConfig config, string name, Type type)
{
//Only compare specific member names
if (config.MembersToIncludeSet.Count > 0 && !config.MembersToIncludeSet.Contains(name))
return true;
if (config.MembersToIgnoreSet.Count > 0)
{
//Ignore by type.membername
if (type != null
&& config.MembersToIgnoreSet.Contains(type.Name + "." + name))
return true;
//Ignore exactly by the name of the member
if (config.MembersToIgnoreSet.Count > 0 && config.MembersToIgnoreSet.Contains(name))
return true;
//Wildcard member
if (config.HasWildcardMembersToExclude() && ExcludedByWildcard(config, name))
return true;
}
return false;
}
/// <summary>
/// Returns true if the property or field should be excluded
/// </summary>
/// <param name="config"></param>
/// <param name="info"></param>
/// <param name="objectType"></param>
/// <returns></returns>
public static bool ShouldExcludeMember(ComparisonConfig config, MemberInfo info, Type objectType)
{
//Only compare specific member names
if (config.MembersToIncludeSet.Count > 0 && !config.MembersToIncludeSet.Contains(info.Name))
return true;
if (config.MembersToIgnoreSet.Count > 0)
{
//Ignore by objecttype.membername
if (config.MembersToIgnoreSet.Contains(objectType.Name + "." + info.Name))
return true;
//Ignore by declaringType.membername
if (info.DeclaringType != null
&& config.MembersToIgnoreSet.Contains(info.DeclaringType.Name + "." + info.Name))
return true;
//Ignore exactly by the name of the member
if (config.MembersToIgnoreSet.Count > 0 && config.MembersToIgnoreSet.Contains(info.Name))
return true;
//Wildcard member
if (config.HasWildcardMembersToExclude() && ExcludedByWildcard(config, info.Name))
return true;
}
if (IgnoredByAttribute(config, info))
return true;
if (IgnoredByLackOfAttribute(config, info))
return true;
return false;
}
/// <summary>
/// Returns true if the property or field should be exluded by wilcard
/// </summary>
/// <param name="config"></param>
/// <param name="name"></param>
/// <returns></returns>
public static bool ExcludedByWildcard(ComparisonConfig config, string name)
{
foreach (var memberWildcard in config.MembersToIgnore)
{
string small;
if (memberWildcard.StartsWith("*") && memberWildcard.EndsWith("*") && memberWildcard.Length > 2)
{
small = memberWildcard.Substring(1, memberWildcard.Length - 2);
if (name.Contains(small))
{
return true;
}
}
else if (memberWildcard.StartsWith("*")
&& memberWildcard.Length >= 2
&& name.EndsWith(memberWildcard.Substring(1)))
{
return true;
}
else if (memberWildcard.EndsWith("*")
&& memberWildcard.Length >= 2
&& name.StartsWith(memberWildcard.Substring(0, memberWildcard.Length - 2)))
{
return true;
}
}
return false;
}
/// <summary>
/// Check if the class should be exluded by Attribute
/// </summary>
/// <param name="config"></param>
/// <param name="t1"></param>
/// <param name="t2"></param>
/// <returns></returns>
public static bool ShouldExcludeClass(ComparisonConfig config, Type t1, Type t2)
{
//Only include specific class types
if (config.ClassTypesToIncludeSet.Count > 0
&& (!config.ClassTypesToIncludeSet.Contains(t1)
|| !config.ClassTypesToIncludeSet.Contains(t2)))
{
return true;
}
//Ignore specific class types
if (config.ClassTypesToIgnoreSet.Count > 0
&& (config.ClassTypesToIgnoreSet.Contains(t1)
|| config.ClassTypesToIgnoreSet.Contains(t2)))
{
return true;
}
//The class is ignored by an attribute
if (IgnoredByAttribute(config, t1.GetTypeInfo()) || IgnoredByAttribute(config, t2.GetTypeInfo()))
return true;
return false;
}
/// <summary>
/// Check if the class type should be excluded based on the configuration
/// </summary>
/// <param name="config"></param>
/// <param name="t1"></param>
/// <param name="t2"></param>
/// <returns></returns>
public static bool ShouldExcludeType(ComparisonConfig config, Type t1, Type t2)
{
//Only include specific types
if (config.TypesToIncludeSet.Count > 0
&& (!config.TypesToIncludeSet.Contains(t1)
|| !config.TypesToIncludeSet.Contains(t2)))
{
return true;
}
//Ignore specific types
if (config.TypesToIgnoreSet.Count > 0
&& (config.TypesToIgnoreSet.Contains(t1)
|| config.TypesToIgnoreSet.Contains(t2)))
{
return true;
}
return false;
}
/// <summary>
/// Check if any type has attributes that should be bypassed
/// </summary>
/// <returns></returns>
public static bool IgnoredByAttribute(ComparisonConfig config, MemberInfo info)
{
//Prevent loading attributes when AttributesToIgnore is empty
if (config.AttributesToIgnoreSet.Count == 0)
{
return false;
}
var attributes = info.GetCustomAttributes(true);
return attributes.Any(a => config.AttributesToIgnoreSet.Contains(a.GetType()));
}
/// <summary>
/// Check if any type lacks attributes that should be required
/// </summary>
/// <returns></returns>
public static bool IgnoredByLackOfAttribute(ComparisonConfig config, MemberInfo info)
{
//Prevent loading attributes when RequiredAttributesToCompare is empty
if (config.RequiredAttributesToCompareSet.Count == 0)
{
return false;
}
var attributes = info.GetCustomAttributes(true);
return !attributes.Any(a => config.RequiredAttributesToCompareSet.Contains(a.GetType()));
}
}
}

View File

@@ -1,44 +0,0 @@
using System;
using System.IO;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Helper methods for files and directories
/// </summary>
public static class FileHelper
{
/// <summary>
/// Get the current directory of the executing assembly
/// </summary>
/// <returns></returns>
public static string GetCurrentDirectory()
{
#if NETSTANDARD
string basePath = AppContext.BaseDirectory;
#else
string basePath = AppDomain.CurrentDomain.BaseDirectory;
#endif
return PathSlash(basePath);
}
/// <summary>
/// Ensure the passed string ends with a directory separator character unless the string is blank.
/// </summary>
/// <param name="path">The string to append the backslash to.</param>
/// <returns>String with a "/" on the end</returns>
public static String PathSlash(string path)
{
string separator = Convert.ToString(Path.DirectorySeparatorChar);
if (path.Length == 0)
return path;
else if (path.EndsWith(separator))
return path;
else
return path + separator;
}
}
}

View File

@@ -1,118 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Config object for HtmlReport
/// </summary>
public class HtmlConfig
{
/// <summary>
/// The header value of the Bread Crumb column
/// </summary>
public string BreadCrumbColumName { get; set; }
/// <summary>
/// The header value of the Expected column
/// </summary>
public string ExpectedColumnName { get; set; }
/// <summary>
/// The header value of the Actual column
/// </summary>
public string ActualColumnName { get; set; }
/// <summary>
/// If true, the output will be complete html, if false, it will just be the table
/// </summary>
public bool GenerateFullHtml { get; set; }
/// <summary>
/// Setting this will overwrite the default html header (html, head, body tags)
/// </summary>
public string HtmlHeader
{
get { return _htmlHeader.Replace("%TITLE%", HtmlTitle).Replace("%CSS%", Style); }
set { _htmlHeader = value; }
}
/// <summary>
/// Setting this will overwrite the default html footer (closing body, html tags)
/// </summary>
public string HtmlFooter { get; set; }
/// <summary>
/// The title of the page - only visible if GenerateFullHtml == true
/// </summary>
public string HtmlTitle { get; set; }
/// <summary>
/// The CSS Style of the page - only used if the GenerateFullHtml == true
/// </summary>
public string Style { get; set; }
private string _htmlHeader;
/// <summary>
/// Default constructor, sets default values
/// </summary>
public HtmlConfig()
{
// set all the defaults
BreadCrumbColumName = "Bread Crumb";
ExpectedColumnName = "Expected";
ActualColumnName = "Actual";
GenerateFullHtml = false;
HtmlTitle = "Document";
_htmlHeader = _header;
HtmlFooter = _footer;
Style = _css;
}
/// <summary>
/// Appends to the existing Style value
/// </summary>
/// <param name="css">Any css to append</param>
public void IncludeCustomCSS(string css)
{
Style += css;
}
/// <summary>
/// Replaces the existing Style value
/// </summary>
/// <param name="css">Any css to use</param>
public void ReplaceCSS(string css)
{
Style = css;
}
private const string _header = @"<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<meta http-equiv=""X-UA-Compatible"" content=""ie=edge"">
<title>%TITLE%</title>
<style>
%CSS%
</style>
</head>
<body>
";
private const string _footer = @"</body>
</html>";
private const string _css = @"
//.diff-crumb {background: gray;}
.diff-table tr:nth-child(odd) { background-color:#eee; }
.diff-table tr:nth-child(even) { background-color:#fff; }
//.diff-expected {color: red;}
.diff-actual {color:red;}
";
}
}

View File

@@ -1,30 +0,0 @@
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Interface for mocking
/// </summary>
public interface ICompareLogic
{
/// <summary>
/// The default configuration
/// </summary>
ComparisonConfig Config { get; set; }
/// <summary>
/// Compare two objects of the same type to each other.
/// </summary>
/// <remarks>
/// Check the Differences or DifferencesString Properties for the differences.
/// Default MaxDifferences is 1 for performance
/// </remarks>
/// <param name="expectedObject">The expected object value to compare</param>
/// <param name="actualObject">The actual object value to compare</param>
/// <returns>True if they are equal</returns>
ComparisonResult Compare(object expectedObject, object actualObject);
/// <summary>
/// Reflection properties and fields are cached. By default this cache is cleared automatically after each compare.
/// </summary>
void ClearCache();
}
}

View File

@@ -1,332 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using FSI.Lib.CompareNetObjects;
using FSI.Lib.CompareNetObjects.TypeComparers;
namespace FSI.Lib.CompareNetObjects.IgnoreOrderTypes
{
/// <summary>
/// Logic for comparing lists that are out of order based on a key
/// </summary>
public class IgnoreOrderLogic : BaseComparer
{
private readonly RootComparer _rootComparer;
/// <summary>
/// Initializes a new instance of the <see cref="IgnoreOrderLogic"/> class.
/// </summary>
/// <param name="rootComparer">The root comparer.</param>
public IgnoreOrderLogic(RootComparer rootComparer)
{
_rootComparer = rootComparer;
}
/// <summary>
/// Compares the enumerators and ignores the order
/// </summary>
public void CompareEnumeratorIgnoreOrder(CompareParms parms, bool countsDifferent)
{
CompareOutOfOrder(parms, false);
}
private class InstanceCounter
{
public InstanceCounter(object value, int counter)
{
Counter = counter;
ObjectValue = value;
}
public int Counter;
public readonly object ObjectValue;
}
private void CompareOutOfOrder(CompareParms parms, bool reverseCompare)
{
bool differenceDetected = false;
int list1Count = 0;
int list2Count = 0;
IEnumerator enumerator1;
IEnumerator enumerator2;
var list1 = new Dictionary<string, InstanceCounter>();
var list2 = new Dictionary<string, InstanceCounter>();
// Store matching spec for each type.
var matchingSpec1 = new Dictionary<Type, List<string>>();
var matchingSpec2 = new Dictionary<Type, List<string>>();
// Determine an explicit fallback to be used if the first element in an enumerable is null.
Type fallbackType1 = parms.Object1Type != null ? (TypeHelper.IsGenericType(parms.Object1Type) ? parms.Object1Type.GetGenericArguments()[0] : parms.Object1Type.GetElementType()) : null;
Type fallbackType2 = parms.Object2Type != null ? (TypeHelper.IsGenericType(parms.Object2Type) ? parms.Object2Type.GetGenericArguments()[0] : parms.Object2Type.GetElementType()) : null;
if (!reverseCompare)
{
enumerator1 = ((IEnumerable)parms.Object1).GetEnumerator();
enumerator2 = ((IEnumerable)parms.Object2).GetEnumerator();
}
else
{
enumerator1 = ((IEnumerable)parms.Object2).GetEnumerator();
enumerator2 = ((IEnumerable)parms.Object1).GetEnumerator();
}
while (enumerator1.MoveNext())
{
var data = enumerator1.Current;
if (data != null
&& parms.Config.ClassTypesToIgnore.Contains(data.GetType()))
{
continue;
}
var dataType1 = data?.GetType() ?? fallbackType1;
if (!matchingSpec1.ContainsKey(dataType1))
matchingSpec1.Add(dataType1, GetMatchingSpec(parms.Result, dataType1));
var matchingIndex = GetMatchIndex(parms.Result, matchingSpec1[dataType1], data);
if (!list1.ContainsKey(matchingIndex))
list1.Add(matchingIndex, new InstanceCounter(data, 1));
else
list1[matchingIndex].Counter++;
list1Count++;
}
while (enumerator2.MoveNext())
{
var data = enumerator2.Current;
if (data != null
&& parms.Config.ClassTypesToIgnore.Contains(data.GetType()))
{
continue;
}
var dataType2 = data?.GetType() ?? fallbackType2;
if (!matchingSpec2.ContainsKey(dataType2))
matchingSpec2.Add(dataType2, GetMatchingSpec(parms.Result, dataType2));
var matchingIndex = GetMatchIndex(parms.Result, matchingSpec2[dataType2], data);
if (!list2.ContainsKey(matchingIndex))
list2.Add(matchingIndex, new InstanceCounter(data, 1));
else
list2[matchingIndex].Counter++;
list2Count++;
}
while (list1.Count > 0)
{
KeyValuePair<string, InstanceCounter> item1 = list1.First();
string currentBreadCrumb = $"{parms.BreadCrumb}[{item1.Key}]";
bool bothObjectValuesNull = item1.Value.ObjectValue == null
&& list2.ContainsKey(item1.Key)
&& list2[item1.Key].ObjectValue == null;
object item2Value = list2.ContainsKey(item1.Key) ? list2[item1.Key].ObjectValue : null;
if (bothObjectValuesNull)
{
if (--list2[item1.Key].Counter == 0)
list2.Remove(item1.Key); // Matched, so remove from dictionary so we don't double-dip on it
}
else if (item2Value != null)
{
CompareParms childParams = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = item1.Value.ObjectValue,
Object2 = item2Value,
BreadCrumb = currentBreadCrumb
};
_rootComparer.Compare(childParams);
if (--list2[item1.Key].Counter == 0)
list2.Remove(item1.Key); // Matched, so remove from dictionary so we don't double-dip on it
}
else
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = currentBreadCrumb,
Object1Value = reverseCompare ? "(null)" : NiceString(item1.Value.ObjectValue),
Object2Value = reverseCompare ? NiceString(item1.Value.ObjectValue) : "(null)",
ChildPropertyName = "Item",
Object1 = reverseCompare ? null : item1.Value.ObjectValue,
Object2 = reverseCompare ? item1.Value.ObjectValue : null
};
AddDifference(parms.Result, difference);
differenceDetected = true;
}
if (parms.Result.ExceededDifferences)
return;
if (--list1[item1.Key].Counter == 0)
list1.Remove(item1.Key);
}
while (list2.Count > 0)
{
var item2 = list2.First();
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = $"{parms.BreadCrumb}[{item2.Key}]",
Object1Value = reverseCompare ? NiceString(item2.Value.ObjectValue) : "(null)",
Object2Value = reverseCompare ? "(null)" : NiceString(item2.Value.ObjectValue),
ChildPropertyName = "Item",
Object1 = reverseCompare ? item2.Value.ObjectValue : null,
Object2 = reverseCompare ? null : item2.Value.ObjectValue
};
AddDifference(parms.Result, difference);
differenceDetected = true;
list2.Remove(item2.Key);
if (parms.Result.ExceededDifferences)
return;
}
//This use case one of the lists has a duplicate value
if (!differenceDetected && list1Count != list2Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = list1Count.ToString(CultureInfo.InvariantCulture),
Object2Value = list2Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
}
}
private string GetMatchIndex(ComparisonResult result, List<string> spec, object currentObject)
{
if (currentObject == null)
return "(null)";
List<PropertyInfo> properties = Cache.GetPropertyInfo(result.Config, currentObject.GetType()).ToList();
StringBuilder sb = new StringBuilder();
foreach (var item in spec)
{
var info = properties.FirstOrDefault(o => o.Name == item);
if (info == null)
{
throw new Exception(
$"Invalid CollectionMatchingSpec. No such property {item} for type {currentObject.GetType().Name} ");
}
// Ensure that we will not compare indexer property.
var indexParameters = info.GetIndexParameters();
if (indexParameters.Length > 0)
{
throw new ArgumentException(
$"Invalid object {currentObject.GetType().Name} to compare. Object with indexers cannot be compared when IgnoreCollectionOrder = true.",
nameof(currentObject)
);
}
var propertyValue = info.GetValue(currentObject, null);
if (result.Config.TreatStringEmptyAndNullTheSame && info.PropertyType == typeof(string) && propertyValue == null)
{
propertyValue = string.Empty;
}
else if (propertyValue != null && result.Config.CaseSensitive == false && info.PropertyType == typeof(string))
{
propertyValue = ((string)propertyValue).ToLowerInvariant();
}
if (propertyValue == null)
{
sb.AppendFormat("{0}:(null),", item);
}
else
{
var decimals = BitConverter.GetBytes(decimal.GetBits(result.Config.DecimalPrecision)[3])[2];
var formatString = $"{{0}}:{{1{(TypeHelper.IsDecimal(propertyValue) ? $":N{decimals}" : string.Empty)}}},";
sb.Append(string.Format(formatString, item, propertyValue));
}
}
if (sb.Length == 0)
sb.Append(RespectNumberToString(currentObject));
return sb.ToString().TrimEnd(',');
}
private static string RespectNumberToString(object o)
{
#if NETSTANDARD
string typeString = o.GetType().Name;
switch (typeString)
{
case "Decimal":
return ((decimal)o).ToString("G29");
case "Double":
return ((double)o).ToString("G");
case "Single":
return ((float)o).ToString("G");
default:
return o.ToString();
}
#else
switch (Type.GetTypeCode(o.GetType()))
{
case TypeCode.Decimal:
return ((decimal)o).ToString("G29");
case TypeCode.Double:
return ((double)o).ToString("G");
case TypeCode.Single:
return ((float)o).ToString("G");
default:
return o.ToString();
}
#endif
}
private List<string> GetMatchingSpec(ComparisonResult result, Type type)
{
if (type == null)
return new List<string> { "(null)" };
//The user defined a key for the order
var matchingBasePresent = result.Config.CollectionMatchingSpec.Keys.FirstOrDefault(k => k.IsAssignableFrom(type));
if (matchingBasePresent != null)
{
return result.Config.CollectionMatchingSpec.First(p => p.Key == matchingBasePresent).Value.ToList();
}
//Make a key out of primitive types, date, decimal, string, guid, and enum of the class
List<string> list = Cache.GetPropertyInfo(result.Config, type)
.Where(o => o.CanWrite && !ExcludeLogic.ShouldExcludeMember(result.Config, o, o.DeclaringType) && (TypeHelper.IsSimpleType(o.PropertyType) || TypeHelper.IsEnum(o.PropertyType)))
.Select(o => o.Name).ToList();
return list;
}
}
}

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects.IgnoreOrderTypes
{
internal class IndexerCollectionLooper : IEnumerable
{
private readonly object _indexer;
private readonly PropertyInfo _info;
private readonly int _cnt;
public IndexerCollectionLooper(object obj, PropertyInfo info, int cnt)
{
_indexer = obj;
if (info == null)
throw new ArgumentNullException("info");
_info = info;
_cnt = cnt;
}
public IEnumerator GetEnumerator()
{
for (var i = 0; i < _cnt; i++)
{
object value = _info.GetValue(_indexer, new object[] { i });
yield return value;
}
}
}
}

View File

@@ -1,43 +0,0 @@
// LogicEqualityComparer: Initial contribution by David Rieman
using System.Collections.Generic;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>Implements methods to support the comparison of objects for equality, in a customizable fashion.</summary>
public class LogicEqualityComparer : LogicEqualityComparer<object> { }
/// <summary>Implements methods to support the comparison of objects for equality, in a customizable fashion.</summary>
/// <typeparam name="T">The comparison object type.</typeparam>
public class LogicEqualityComparer<T> : IEqualityComparer<T>
{
private readonly CompareLogic _comparer = new CompareLogic();
/// <summary>Defines the configuration and logic by which Equals comparisons will be performed.</summary>
public CompareLogic CompareLogic
{
get { return _comparer; }
}
/// <summary>Gets or sets a value indicating whether the base object hashes should be used.</summary>
/// <remarks>
/// False by default to allow CompareLogic to evaluate equivalence of otherwise instance-sensitive hashing objects.
/// NOTE: Any object which doesn't override GetHashCode will behave this way, so this property should generally be left false.
/// </remarks>
public bool UseObjectHashes { get; set; }
/// <summary>Compare two objects of the same type to each other.</summary>
/// <returns>True if the objects are considered equivalent, according to the current CompareLogic.</returns>
public bool Equals(T x, T y)
{
return CompareLogic.Compare(x, y).AreEqual;
}
/// <summary>Retrieves the hash of the specified object.</summary>
/// <param name="obj">The object to retrieve a hash for.</param>
public int GetHashCode(T obj)
{
return UseObjectHashes ? obj.GetHashCode() : 0;
}
}
}

View File

@@ -1,69 +0,0 @@
#if !NETSTANDARD1
using System;
using System.Diagnostics;
using System.IO;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Helper methods for processes
/// </summary>
public static class ProcessHelper
{
/// <summary>
/// Execute an external program.
/// </summary>
/// <param name="executablePath">Path and filename of the executable.</param>
/// <param name="arguments">Arguments to pass to the executable.</param>
/// <param name="windowStyle">Window style for the process (hidden, minimized, maximized, etc).</param>
/// <param name="waitUntilFinished">Wait for the process to finish.</param>
/// <returns>Exit Code</returns>
public static int Shell(string executablePath, string arguments, ProcessWindowStyle windowStyle, bool waitUntilFinished)
{
string fileName = "";
try
{
Process process = new Process();
string assemblyPath = Path.Combine(FileHelper.GetCurrentDirectory(), Path.GetFileName(executablePath) ?? string.Empty);
//Look for the file in the executing assembly directory
if (File.Exists(assemblyPath))
{
fileName = assemblyPath;
process.StartInfo.FileName = assemblyPath;
}
else // if there is no path to the file, an error will be thrown
{
fileName = executablePath;
process.StartInfo.FileName = executablePath;
}
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = true;
process.StartInfo.WindowStyle = windowStyle;
//Start the Process
process.Start();
if (waitUntilFinished)
{
process.WaitForExit();
}
if (waitUntilFinished)
return process.ExitCode;
return 0;
}
catch
{
string message = string.Format("Shell Fail: {0} {1}", fileName, arguments);
throw new ApplicationException(message);
}
}
}
}
#endif

View File

@@ -1,216 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FSI.Lib.CompareNetObjects.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Specialized.StringCollection MembersToIgnore {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["MembersToIgnore"]));
}
set {
this["MembersToIgnore"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Specialized.StringCollection AttributesToIgnore {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["AttributesToIgnore"]));
}
set {
this["AttributesToIgnore"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareStaticFields {
get {
return ((bool)(this["CompareStaticFields"]));
}
set {
this["CompareStaticFields"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareStaticProperties {
get {
return ((bool)(this["CompareStaticProperties"]));
}
set {
this["CompareStaticProperties"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool ComparePrivateProperties {
get {
return ((bool)(this["ComparePrivateProperties"]));
}
set {
this["ComparePrivateProperties"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool ComparePrivateFields {
get {
return ((bool)(this["ComparePrivateFields"]));
}
set {
this["ComparePrivateFields"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareChildren {
get {
return ((bool)(this["CompareChildren"]));
}
set {
this["CompareChildren"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareReadOnly {
get {
return ((bool)(this["CompareReadOnly"]));
}
set {
this["CompareReadOnly"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareFields {
get {
return ((bool)(this["CompareFields"]));
}
set {
this["CompareFields"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool CompareProperties {
get {
return ((bool)(this["CompareProperties"]));
}
set {
this["CompareProperties"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool Caching {
get {
return ((bool)(this["Caching"]));
}
set {
this["Caching"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool AutoClearCache {
get {
return ((bool)(this["AutoClearCache"]));
}
set {
this["AutoClearCache"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1")]
public int MaxDifferences {
get {
return ((int)(this["MaxDifferences"]));
}
set {
this["MaxDifferences"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool IgnoreCollectionOrder {
get {
return ((bool)(this["IgnoreCollectionOrder"]));
}
set {
this["IgnoreCollectionOrder"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool IgnoreUnknownObjectTypes {
get {
return ((bool)(this["IgnoreUnknownObjectTypes"]));
}
set {
this["IgnoreUnknownObjectTypes"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool IgnoreObjectDisposedException {
get {
return ((bool)(this["IgnoreObjectDisposedException"]));
}
set {
this["IgnoreObjectDisposedException"] = value;
}
}
}
}

View File

@@ -1,54 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="FSI.Lib.CompareNetObjects.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="MembersToIgnore" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="AttributesToIgnore" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="CompareStaticFields" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="CompareStaticProperties" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ComparePrivateProperties" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ComparePrivateFields" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="CompareChildren" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="CompareReadOnly" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="CompareFields" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="CompareProperties" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="Caching" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoClearCache" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="MaxDifferences" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="IgnoreCollectionOrder" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="IgnoreUnknownObjectTypes" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="IgnoreObjectDisposedException" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Generic class for holding a Property Info, or Dynamic Info
/// </summary>
public class PropertyEntity
{
/// <summary>
/// Constructor
/// </summary>
public PropertyEntity()
{
Indexers = new List<ParameterInfo>();
}
/// <summary>
/// If true, this is a dynamic property
/// </summary>
public bool IsDynamic { get; set; }
/// <summary>
/// Name of the property
/// </summary>
public string Name { get; set; }
/// <summary>
/// Value of the property
/// </summary>
public object Value { get; set; }
/// <summary>
/// Let me reflect on this day
/// </summary>
public Type ReflectedType { get; set; }
/// <summary>
/// The type of the parent
/// </summary>
public Type DeclaringType { get; set; }
/// <summary>
/// The type of the property
/// </summary>
public Type PropertyType { get; set; }
/// <summary>
/// If the property can be read from
/// </summary>
public bool CanRead { get; set; }
/// <summary>
/// If the property can be written to
/// </summary>
public bool CanWrite { get; set; }
/// <summary>
/// Indexers for the property
/// </summary>
public List<ParameterInfo> Indexers { get; set; }
/// <summary>
/// Reference to the property info
/// </summary>
public PropertyInfo PropertyInfo { get; set; }
}
}

View File

@@ -1,78 +0,0 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Abstract Base Duel File Report that has default Output
/// </summary>
public abstract class BaseDualFileReport : IDualFileReport
{
/// <summary>
/// Create two difference files and compare in WinMerge
/// </summary>
/// <param name="differences">A list of the differences</param>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
public virtual void OutputFiles(List<Difference> differences, string expectedFilePath, string actualFilePath)
{
if (differences == null)
throw new ArgumentNullException("differences");
StringBuilder sb1 = new StringBuilder(differences.Count*80);
StringBuilder sb2 = new StringBuilder(differences.Count*80);
bool anyLongerThan80 = differences.Any(o =>
o.Object1Value.Replace(Environment.NewLine, "|").Length > 80
|| o.Object2Value.Replace(Environment.NewLine, "|").Length > 80);
foreach (var difference in differences)
{
if (anyLongerThan80)
{
sb1.Append(difference.GetShortItem());
sb1.Append(", ");
sb1.AppendLine(difference.Object1Value.Replace(Environment.NewLine, "|"));
sb2.Append(difference.GetShortItem());
sb2.Append(", ");
sb2.AppendLine(difference.Object2Value.Replace(Environment.NewLine, "|"));
}
else
{
sb1.Append(difference.Object1Value.Replace(Environment.NewLine, "|"));
sb1.Append(", ");
sb1.AppendLine(difference.GetShortItem());
sb2.Append(difference.Object2Value.Replace(Environment.NewLine, "|"));
sb2.Append(", ");
sb2.AppendLine(difference.GetShortItem());
}
}
if (String.IsNullOrEmpty(Path.GetDirectoryName(expectedFilePath)))
expectedFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), expectedFilePath);
if (String.IsNullOrEmpty(Path.GetDirectoryName(actualFilePath)))
actualFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), actualFilePath);
File.WriteAllText(expectedFilePath, sb1.ToString());
File.WriteAllText(actualFilePath, sb2.ToString());
}
/// <summary>
/// Launch application to compare two files
/// </summary>
/// <param name="expectedFilePath">The path for the expected file results</param>
/// <param name="actualFilePath">The path for the actual file results</param>
public abstract void LaunchApplication(string expectedFilePath, string actualFilePath);
}
}

View File

@@ -1,97 +0,0 @@

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Output files and launch Beyond Compare
/// </summary>
public class BeyondCompareReport : BaseDualFileReport
{
private const string APPLICATION_NAME = "BCompare.exe";
#if NETSTANDARD1
/// <summary>
/// Throw a NotSupported exception if we are running under .NET Standard 1.0
/// </summary>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
public override void LaunchApplication(string expectedFilePath, string actualFilePath)
{
throw new NotSupportedException();
}
#else
/// <summary>
/// Launch Beyond Compare
/// </summary>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
public override void LaunchApplication(string expectedFilePath, string actualFilePath)
{
if (!EnvironmentHelper.IsWindows())
throw new NotSupportedException();
if (String.IsNullOrEmpty(Path.GetDirectoryName(expectedFilePath)))
expectedFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), expectedFilePath);
if (String.IsNullOrEmpty(Path.GetDirectoryName(actualFilePath)))
actualFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), actualFilePath);
string args = string.Format("\"{0}\" \"{1}\"", expectedFilePath, actualFilePath);
string beyondComparePath = FindBeyondCompare();
if (String.IsNullOrEmpty(beyondComparePath))
throw new FileNotFoundException(APPLICATION_NAME);
ProcessHelper.Shell(beyondComparePath, args, ProcessWindowStyle.Normal, false);
}
#endif
/// <summary>
/// Find the path of the Beyond Compare executable
/// </summary>
/// <returns>The path or null if not found</returns>
public string FindBeyondCompare()
{
//It should be in the Program Files (x86) directory
string programFilesPath = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
if (!String.IsNullOrEmpty(programFilesPath))
{
string[] directories = Directory.GetDirectories(programFilesPath, "Beyond Compare*");
foreach (var directory in directories.OrderByDescending(o => o))
{
string[] files = Directory.GetFiles(directory, APPLICATION_NAME);
if (files.Any())
return files.First();
}
}
programFilesPath = Environment.GetEnvironmentVariable("ProgramFiles");
if (!String.IsNullOrEmpty(programFilesPath))
{
string[] directories = Directory.GetDirectories(programFilesPath, "Beyond Compare*");
foreach (var directory in directories.OrderByDescending(o => o))
{
string[] files = Directory.GetFiles(directory, APPLICATION_NAME);
if (files.Any())
return files.First();
}
}
return null;
}
}
}

View File

@@ -1,131 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Create a CSV file of the differences and launch the default CSV handler (usually Excel)
/// </summary>
public class CsvReport : ISingleFileReport
{
/// <summary>
/// Output the differences to a file
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="filePath">The file path</param>
public void OutputFile(List<Difference> differences, string filePath)
{
if (String.IsNullOrEmpty(Path.GetDirectoryName(filePath)))
filePath = Path.Combine(FileHelper.GetCurrentDirectory(), filePath);
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (TextWriter writer = new StreamWriter(fileStream))
{
WriteItOut(differences, writer);
}
}
}
private void WriteItOut(List<Difference> differences, TextWriter writer)
{
writer.WriteLine("Bread Crumb,Expected,Actual");
foreach (var difference in differences)
{
writer.Write("{0},", EscapeString(difference.GetShortItem()));
writer.Write("{0},", EscapeString(difference.Object1Value));
writer.WriteLine("{0}", EscapeString(difference.Object2Value));
}
}
/// <summary>
/// Output the differences to a stream
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="stream">An output stream</param>
public void OutputStream(List<Difference> differences, Stream stream)
{
TextWriter writer = new StreamWriter(stream);
WriteItOut(differences, writer);
writer.Flush();
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Output the differences to a string
/// </summary>
/// <param name="differences">A list of differences</param>
/// <returns>A string</returns>
public string OutputString(List<Difference> differences)
{
StringBuilder sb = new StringBuilder(differences.Count*40);
TextWriter writer = new StringWriter(sb);
WriteItOut(differences, writer);
return sb.ToString();
}
#if NETSTANDARD1
/// <summary>
/// Throws a NotSupportedException when run under .NET Standard 1
/// </summary>
/// <param name="filePath">The differences file</param>
public void LaunchApplication(string filePath)
{
throw new NotSupportedException();
}
#else
/// <summary>
/// Launch the application associated with CSV files
/// </summary>
/// <param name="filePath">The differences file</param>
public void LaunchApplication(string filePath)
{
if (!EnvironmentHelper.IsWindows())
throw new NotSupportedException();
ProcessHelper.Shell(filePath, string.Empty, ProcessWindowStyle.Normal, false);
}
#endif
/// <summary>
/// Escape special characters
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private string EscapeString(object value)
{
if (value == null)
return string.Empty;
string data = value.ToString();
// CSV rules: http://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules
// From the rules:
// 1. if the data has quote, escape the quote in the data
// 2. if the data contains the delimiter (in our case ','), double-quote it
// 3. if the data contains the new-line, double-quote it.
if (data.Contains("\""))
{
data = data.Replace("\"", "\"\"");
}
if (data.Contains(",") || data.Contains(Environment.NewLine))
{
data = String.Format("{0}{1}{2}", "\"", data, "\"");
}
return data;
}
}
}

View File

@@ -1,142 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Create an HTML file of the differences and launch the default HTML handler
/// </summary>
public class HtmlReport : ISingleFileReport
{
/// <summary>
/// Default constructor, sets up Config object
/// </summary>
public HtmlReport()
{
Config = new HtmlConfig();
}
/// <summary>
/// HtmlReport Configuration
/// </summary>
public HtmlConfig Config { get; set; }
/// <summary>
/// Output the differences to a file
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="filePath">The file path</param>
public void OutputFile(List<Difference> differences, string filePath)
{
if (String.IsNullOrEmpty(Path.GetDirectoryName(filePath)))
filePath = Path.Combine(FileHelper.GetCurrentDirectory(), filePath);
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (TextWriter writer = new StreamWriter(fileStream))
{
WriteItOut(differences, writer);
}
}
}
private void WriteItOut(List<Difference> differences, TextWriter writer)
{
if (Config.GenerateFullHtml)
{
writer.Write(Config.HtmlHeader);
}
writer.WriteLine("<table class=\"diff-table\">");
writer.WriteLine("<thead><th>{0}</th><th>{1}</th><th>{2}</th></thead>",
Config.BreadCrumbColumName,
Config.ExpectedColumnName,
Config.ActualColumnName);
foreach (var difference in differences)
{
writer.WriteLine(
"<tr><td class=\"diff-crumb\">{0}</td><td class=\"diff-expected\">{1}</td><td class=\"diff-actual\">{2}</td></tr>",
EscapeString(difference.GetShortItem()),
EscapeString(difference.Object1Value),
EscapeString(difference.Object2Value));
}
writer.WriteLine("</table>");
if (Config.GenerateFullHtml)
{
writer.Write(Config.HtmlFooter);
}
}
#if NETSTANDARD1
/// <summary>
/// Throw a NotSupported exception if we are running under .NET Standard 1.0
/// </summary>
/// <param name="filePath">The differences file</param>
public void LaunchApplication(string filePath)
{
throw new NotSupportedException();
}
#else
/// <summary>
/// Launch the HTML Report
/// </summary>
/// <param name="filePath">The differences file</param>
public void LaunchApplication(string filePath)
{
if (!EnvironmentHelper.IsWindows())
throw new NotSupportedException();
ProcessHelper.Shell(filePath, string.Empty, ProcessWindowStyle.Normal, false);
}
#endif
/// <summary>
/// Output the differences to a stream
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="stream">An output stream</param>
public void OutputStream(List<Difference> differences, Stream stream)
{
TextWriter writer = new StreamWriter(stream);
WriteItOut(differences, writer);
writer.Flush();
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Output the differences to a string
/// </summary>
/// <param name="differences">A list of differences</param>
/// <returns>A string</returns>
public string OutputString(List<Difference> differences)
{
StringBuilder sb = new StringBuilder(differences.Count * 40);
TextWriter writer = new StringWriter(sb);
WriteItOut(differences, writer);
return sb.ToString();
}
/// <summary>
/// Escape special characters
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private string EscapeString(object value)
{
if (value == null)
return string.Empty;
return WebHelper.HtmlEncode(value.ToString());
}
}
}

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Define a dual file report like Beyond Compare, WinMerge etc.
/// </summary>
public interface IDualFileReport
{
/// <summary>
/// Output the differences to two files
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
void OutputFiles(List<Difference> differences, string expectedFilePath, string actualFilePath);
/// <summary>
/// Launch the comparison application
/// </summary>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
void LaunchApplication(string expectedFilePath, string actualFilePath);
}
}

View File

@@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Defines a Single File Report
/// </summary>
public interface ISingleFileReport
{
/// <summary>
/// Output the differences to a file
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="filePath">The file path</param>
void OutputFile(List<Difference> differences, string filePath);
/// <summary>
/// Output the differences to a stream
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="stream">Where to write to</param>
void OutputStream(List<Difference> differences, Stream stream);
/// <summary>
/// Output the differences to a string
/// </summary>
/// <param name="differences">A list of differences</param>
/// <returns>A string</returns>
string OutputString(List<Difference> differences);
/// <summary>
/// Launch the application for showing the file
/// </summary>
/// <param name="filePath">The file path</param>
void LaunchApplication(string filePath);
}
}

View File

@@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Report for showing differences to an end user
/// </summary>
public class UserFriendlyReport : ISingleFileReport
{
/// <summary>
/// Initializes a new instance of the <see cref="UserFriendlyReport"/> class.
/// </summary>
public UserFriendlyReport()
{
ChangedToText = "CHANGED TO ->";
}
/// <summary>
/// The text in between the values. Defaults to: CHANGED TO ->
/// </summary>
public string ChangedToText { get; set; }
/// <summary>
/// Output the differences to a file
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="filePath">The file path</param>
public void OutputFile(List<Difference> differences, string filePath)
{
if (String.IsNullOrEmpty(Path.GetDirectoryName(filePath)))
filePath = Path.Combine(FileHelper.GetCurrentDirectory(), filePath);
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (TextWriter writer = new StreamWriter(fileStream))
{
WriteItOut(differences, writer);
}
}
}
private string FormatProperty(Difference difference)
{
string shortItem = difference.GetShortItem();
StringBuilder sb = new StringBuilder(shortItem.Length);
string[] words = shortItem.Split(new [] {'.'}, StringSplitOptions.RemoveEmptyEntries);
foreach (var word in words)
{
sb.Append(StringHelper.InsertSpaces(word));
sb.Append(" ");
}
return sb.ToString().Trim();
}
private void WriteItOut(List<Difference> differences, TextWriter writer)
{
foreach (var difference in differences)
{
writer.Write("{0}: ", FormatProperty(difference));
writer.Write("{0} {1} ", difference.Object1Value, ChangedToText);
writer.WriteLine("{0}", difference.Object2Value);
}
}
/// <summary>
/// Output the differences to a stream
/// </summary>
/// <param name="differences">A list of differences</param>
/// <param name="stream">Where to write to</param>
public void OutputStream(List<Difference> differences, Stream stream)
{
TextWriter writer = new StreamWriter(stream);
WriteItOut(differences, writer);
writer.Flush();
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Output the differences to a string
/// </summary>
/// <param name="differences">A list of differences</param>
/// <returns>A string</returns>
public string OutputString(List<Difference> differences)
{
StringBuilder sb = new StringBuilder(differences.Count * 40);
TextWriter writer = new StringWriter(sb);
WriteItOut(differences,writer);
return sb.ToString();
}
#if NETSTANDARD1
/// <summary>
/// Throws an exception when running under .NET Standard 1.0
/// </summary>
/// <param name="filePath">The file path</param>
public void LaunchApplication(string filePath)
{
throw new NotSupportedException();
}
#else
/// <summary>
/// Launch the application for showing the file
/// </summary>
/// <param name="filePath">The file path</param>
public void LaunchApplication(string filePath)
{
if (!EnvironmentHelper.IsWindows())
throw new NotSupportedException();
ProcessHelper.Shell(filePath, string.Empty, ProcessWindowStyle.Normal, false);
}
#endif
}
}

View File

@@ -1,120 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
#if !NETSTANDARD
using Microsoft.Win32;
#endif
namespace FSI.Lib.CompareNetObjects.Reports
{
/// <summary>
/// Output files and launch WinMerge
/// </summary>
public class WinMergeReport : BaseDualFileReport
{
private const string APPLICATION_NAME = "WinMergeU.exe";
#if NETSTANDARD1
/// <summary>
/// Throw a NotSupported exception if we are running under .NET Standard 1.0
/// </summary>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
public override void LaunchApplication(string expectedFilePath, string actualFilePath)
{
throw new NotSupportedException();
}
#else
/// <summary>
/// Launch the WinMerge
/// </summary>
/// <param name="expectedFilePath">The path to write the expected results</param>
/// <param name="actualFilePath">The path to write the actual results</param>
public override void LaunchApplication(string expectedFilePath, string actualFilePath)
{
if (!EnvironmentHelper.IsWindows())
throw new NotSupportedException();
if (String.IsNullOrEmpty(Path.GetDirectoryName(expectedFilePath)))
expectedFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), expectedFilePath);
if (String.IsNullOrEmpty(Path.GetDirectoryName(actualFilePath)))
actualFilePath = Path.Combine(FileHelper.GetCurrentDirectory(), actualFilePath);
string args = string.Format("\"{0}\" \"{1}\"", expectedFilePath, actualFilePath);
string winMergePath = FindWinMerge();
if (String.IsNullOrEmpty(winMergePath))
throw new FileNotFoundException(APPLICATION_NAME);
ProcessHelper.Shell(winMergePath, args, ProcessWindowStyle.Normal, false);
}
#endif
#if !NETSTANDARD
/// <summary>
/// Find the path of the WinMerge executable
/// </summary>
/// <returns>The path or null if not found</returns>
public string FindWinMerge()
{
RegistryKey registryKey =
Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WinMergeU.exe");
if (registryKey == null)
return null;
object value = registryKey.GetValue("");
if (value == null)
return null;
return value.ToString();
}
#else
/// <summary>
/// Find the path of the WinMerge executable
/// </summary>
/// <returns>The path or null if not found</returns>
public string FindWinMerge()
{
//It should be in the Program Files (x86) directory
string programFilesPath = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
if (!String.IsNullOrEmpty(programFilesPath))
{
string[] directories = Directory.GetDirectories(programFilesPath, "WinMerge");
foreach (var directory in directories.OrderByDescending(o => o))
{
string[] files = Directory.GetFiles(directory, APPLICATION_NAME);
if (files.Any())
return files.First();
}
}
programFilesPath = Environment.GetEnvironmentVariable("ProgramFiles");
if (!String.IsNullOrEmpty(programFilesPath))
{
string[] directories = Directory.GetDirectories(programFilesPath, "WinMerge");
foreach (var directory in directories.OrderByDescending(o => o))
{
string[] files = Directory.GetFiles(directory, APPLICATION_NAME);
if (files.Any())
return files.First();
}
}
return null;
}
#endif
}
}

View File

@@ -1,135 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FSI.Lib.CompareNetObjects.TypeComparers;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// The base comparer which contains all the type comparers
/// </summary>
public class RootComparer : BaseComparer
{
#region Properties
/// <summary>
/// A list of the type comparers
/// </summary>
internal List<BaseTypeComparer> TypeComparers { get; set; }
#endregion
#region Methods
/// <summary>
/// Compare two objects
/// </summary>
public bool Compare(CompareParms parms)
{
try
{
if (parms.Object1 == null && parms.Object2 == null)
return true;
Type t1 = parms.Object1 != null ? parms.Object1.GetType() : null;
Type t2 = parms.Object2 != null ? parms.Object2.GetType() : null;
if (ExcludeLogic.ShouldExcludeType(parms.Config, t1, t2))
return true;
BaseTypeComparer customComparer = parms.Config.CustomComparers.FirstOrDefault(o => o.IsTypeMatch(t1, t2));
if (customComparer != null)
{
customComparer.CompareType(parms);
}
else if (parms.CustomPropertyComparer != null)
{
parms.CustomPropertyComparer.CompareType(parms);
}
else
{
BaseTypeComparer typeComparer = TypeComparers.FirstOrDefault(o => o.IsTypeMatch(t1, t2));
if (typeComparer != null)
{
if (parms.Config.IgnoreObjectTypes || !TypesDifferent(parms, t1, t2))
{
typeComparer.CompareType(parms);
}
}
else
{
if (EitherObjectIsNull(parms)) return false;
if (!parms.Config.IgnoreObjectTypes && t1 != null)
throw new NotSupportedException("Cannot compare object of type " + t1.Name);
}
}
}
catch (ObjectDisposedException)
{
if (!parms.Config.IgnoreObjectDisposedException)
throw;
return true;
}
return parms.Result.AreEqual;
}
private bool TypesDifferent(CompareParms parms, Type t1, Type t2)
{
//Objects must be the same type and not be null
if (!parms.Config.IgnoreObjectTypes
&& parms.Object1 != null
&& parms.Object2 != null
&& t1 != t2)
{
//Only care if they are in the same inheritance hierarchy or decleared as the same interface.
if (parms.Config.IgnoreConcreteTypes
&& (parms.Object1DeclaredType != null
&& parms.Object2DeclaredType != null
&& parms.Object1DeclaredType == parms.Object2DeclaredType
|| (t1.IsAssignableFrom(t2) || t2.IsAssignableFrom(t1))))
{
return false;
}
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = t1.FullName,
Object2Value = t2.FullName,
ChildPropertyName = "GetType()",
MessagePrefix = "Different Types",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return true;
}
return false;
}
private bool EitherObjectIsNull(CompareParms parms)
{
//Check if one of them is null
if (parms.Object1 == null || parms.Object2 == null)
{
AddDifference(parms);
return true;
}
return false;
}
#endregion
}
}

View File

@@ -1,77 +0,0 @@
using System.Collections.Generic;
using FSI.Lib.CompareNetObjects.TypeComparers;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Factory to create a root comparer
/// </summary>
public static class RootComparerFactory
{
#region Class Variables
private static readonly object _locker = new object();
private static RootComparer _rootComparer;
#endregion
#region Methods
/// <summary>
/// Get the current root comparer
/// </summary>
/// <returns></returns>
public static RootComparer GetRootComparer()
{
lock(_locker)
if (_rootComparer == null)
_rootComparer= BuildRootComparer();
return _rootComparer;
}
private static RootComparer BuildRootComparer()
{
_rootComparer = new RootComparer();
_rootComparer.TypeComparers = new List<BaseTypeComparer>();
_rootComparer.TypeComparers.Add(new StringComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DateComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DecimalComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DoubleComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new PointerComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new SimpleTypeComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new ReadOnlyCollectionComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new ListComparer(_rootComparer));
#if NETFULL || NETSTANDARD2_0 || NETSTANDARD2_1
_rootComparer.TypeComparers.Add(new IpEndPointComparer(_rootComparer));
#endif
_rootComparer.TypeComparers.Add(new RuntimeTypeComparer(_rootComparer));
#if !NETSTANDARD
_rootComparer.TypeComparers.Add(new FontComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DatasetComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DataTableComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DataRowComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DataColumnComparer(_rootComparer));
#endif
_rootComparer.TypeComparers.Add(new EnumerableComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new ByteArrayComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DictionaryComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new HashSetComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new CollectionComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new UriComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new StringBuilderComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new ClassComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new DateTimeOffSetComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new TimespanComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new StructComparer(_rootComparer));
_rootComparer.TypeComparers.Add(new ImmutableArrayComparer(_rootComparer));
return _rootComparer;
}
#endregion
}
}

View File

@@ -1,78 +0,0 @@
using System;
using System.Text;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Methods for manipulating strings
/// </summary>
public static class StringHelper
{
/// <summary>
/// Insert spaces into a string
/// </summary>
/// <example>
/// OrderDetails = Order Details
/// 10Net30 = 10 Net 30
/// FTPHost = FTP Host
/// </example>
/// <param name="input"></param>
/// <returns></returns>
public static string InsertSpaces(string input)
{
bool isLastUpper = true;
if (String.IsNullOrEmpty(input))
return string.Empty;
StringBuilder sb = new StringBuilder(input.Length + (input.Length / 2));
//Replace underline with spaces
input = input.Replace("_", " ");
input = input.Replace(" ", " ");
//Trim any spaces
input = input.Trim();
if (String.IsNullOrEmpty(input))
return string.Empty;
char[] chars = input.ToCharArray();
sb.Append(chars[0]);
for (int i = 1; i < chars.Length; i++)
{
bool isUpperOrNumberOrDash = (chars[i] >= 'A' && chars[i] <= 'Z') || (chars[i] >= '0' && chars[i] <= '9') || chars[i] == '-';
bool isNextCharLower = i < chars.Length - 1 && (chars[i + 1] >= 'a' && chars[i + 1] <= 'z');
bool isSpace = chars[i] == ' ';
bool isLower = (chars[i] >= 'a' && chars[i] <= 'z');
//There was a space already added
if (isSpace)
{
}
//Look for upper case characters that have lower case characters before
//Or upper case characters where the next character is lower
else if ((isUpperOrNumberOrDash && isLastUpper == false)
|| (isUpperOrNumberOrDash && isNextCharLower))
{
sb.Append(' ');
isLastUpper = true;
}
else if (isLower)
{
isLastUpper = false;
}
sb.Append(chars[i]);
}
//Replace double spaces
sb.Replace(" ", " ");
return sb.ToString();
}
}
}

View File

@@ -1,22 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects
{
#if !NETSTANDARD
/// <summary>
/// Extensions for Type to provide backward compatibility between latest and older .net Framework APIs.
/// </summary>
public static class TypeBackwardsCompatibilityExtensions
{
/// <summary>
/// Function to provide compilation compatibility between older code and newer style.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The type.</returns>
public static Type GetTypeInfo(this Type type)
{
return type;
}
}
#endif
}

View File

@@ -1,179 +0,0 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Text;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Common functionality for all Comparers
/// </summary>
public class BaseComparer
{
/// <summary>
/// Add a breadcrumb to an existing breadcrumb
/// </summary>
/// <param name="config">Comparison configuration</param>
/// <param name="existing">The existing breadcrumb</param>
/// <param name="name">The field or property name</param>
/// <returns>The new breadcrumb</returns>
protected string AddBreadCrumb(ComparisonConfig config, string existing, string name)
{
return AddBreadCrumb(config, existing, name, string.Empty, null);
}
/// <summary>
/// Add a breadcrumb to an existing breadcrumb
/// </summary>
/// <param name="config">The comparison configuration</param>
/// <param name="existing">The existing breadcrumb</param>
/// <param name="name">The property or field name</param>
/// <param name="extra">Extra information to output after the name</param>
/// <param name="index">The index for an array, list, or row</param>
/// <returns>The new breadcrumb</returns>
protected string AddBreadCrumb(ComparisonConfig config, string existing, string name, string extra, int index)
{
return AddBreadCrumb(config, existing, name, extra, index >= 0 ? index.ToString(CultureInfo.InvariantCulture) : null);
}
/// <summary>
/// Add a breadcrumb to an existing breadcrumb
/// </summary>
/// <remarks>This originally used a string builder which had lower performance</remarks>
/// <param name="config">Comparison configuration</param>
/// <param name="existing">The existing breadcrumb</param>
/// <param name="name">The field or property name</param>
/// <param name="extra">Extra information to append after the name</param>
/// <param name="index">The index if it is an array, list, row etc.</param>
/// <returns>The new breadcrumb</returns>
protected string AddBreadCrumb(ComparisonConfig config, string existing, string name, string extra, string index)
{
if (config == null)
throw new ArgumentNullException("config");
bool useIndex = !String.IsNullOrEmpty(index);
if (name == null)
throw new ArgumentNullException("name");
bool useName = name.Length > 0;
string stringResult = existing;
if (useName)
{
//Do not put a period at the beginning
if (stringResult.Length > 0)
{
stringResult += ".";
}
stringResult += name;
}
stringResult += extra;
if (useIndex)
{
// ReSharper disable RedundantAssignment
int result = -1;
// ReSharper restore RedundantAssignment
stringResult += String.Format(Int32.TryParse(index, out result) ? "[{0}]" : "[\"{0}\"]", index);
}
if (config.ShowBreadcrumb)
{
#if (DEBUG) || NETSTANDARD
Console.WriteLine(stringResult);
#endif
#if !NETSTANDARD && !DEBUG
Trace.WriteLine(stringResult);
#endif
}
return stringResult;
}
/// <summary>
/// Add a difference for the current parameters
/// </summary>
/// <param name="parameters"></param>
protected void AddDifference(CompareParms parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
Difference difference = new Difference
{
ParentObject1 = parameters.ParentObject1,
ParentObject2 = parameters.ParentObject2,
PropertyName = parameters.BreadCrumb,
Object1Value = NiceString(parameters.Object1),
Object2Value = NiceString(parameters.Object2),
Object1 = parameters.Object1,
Object2 = parameters.Object2
};
AddDifference(parameters.Result,difference);
}
/// <summary>
/// Add a difference to the result
/// </summary>
/// <param name="difference">The difference to add to the result</param>
/// <param name="result">The comparison result</param>
protected void AddDifference(ComparisonResult result, Difference difference)
{
if (result == null)
throw new ArgumentNullException("result");
if (difference == null)
throw new ArgumentNullException("difference");
difference.ActualName = result.Config.ActualName;
difference.ExpectedName = result.Config.ExpectedName;
difference.Object1TypeName = difference.Object1 != null && difference.Object1 != null
? difference.Object1.GetType().Name : "null";
difference.Object2TypeName = difference.Object2 != null && difference.Object2 != null
? difference.Object2.GetType().Name : "null";
result.Differences.Add(difference);
result.Config.DifferenceCallback(difference);
}
/// <summary>
/// Convert an object to a nicely formatted string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected string NiceString(object value)
{
try
{
if (value == null)
return "(null)";
#if !NETSTANDARD
if (value == DBNull.Value)
return "System.DBNull.Value";
#endif
return value.ToString();
}
catch
{
return string.Empty;
}
}
}
}

View File

@@ -1,40 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Common functionality for all Type Comparers
/// </summary>
public abstract class BaseTypeComparer : BaseComparer
{
/// <summary>
/// A reference to the root comparer as newed up by the RootComparerFactory
/// </summary>
public RootComparer RootComparer { get; set; }
/// <summary>
/// Protected constructor that references the root comparer
/// </summary>
/// <param name="rootComparer"></param>
protected BaseTypeComparer(RootComparer rootComparer)
{
RootComparer = rootComparer;
}
/// <summary>
/// If true the type comparer will handle the comparison for the type
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public abstract bool IsTypeMatch(Type type1, Type type2);
/// <summary>
/// Compare the two objects
/// </summary>
public abstract void CompareType(CompareParms parms);
}
}

View File

@@ -1,119 +0,0 @@
using System;
using System.Collections;
using System.Globalization;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two byte arrays
/// </summary>
public class ByteArrayComparer : BaseTypeComparer
{
/// <summary>
/// Protected constructor that references the root comparer
/// </summary>
/// <param name="rootComparer">The root comparer.</param>
public ByteArrayComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// If true the type comparer will handle the comparison for the type
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns><c>true</c> if it is a byte array; otherwise, <c>false</c>.</returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsByteArray(type1)
&& TypeHelper.IsByteArray(type2);
}
/// <summary>
/// Compare two byte array objects
/// </summary>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms == null || parms.Object1 == null || parms.Object2 == null)
return;
if (ListsHaveDifferentCounts(parms))
return;
CompareItems(parms);
}
private bool ListsHaveDifferentCounts(CompareParms parms)
{
IList ilist1 = parms.Object1 as IList;
IList ilist2 = parms.Object2 as IList;
if (ilist1 == null)
throw new ArgumentException("parms.Object1");
if (ilist2 == null)
throw new ArgumentException("parms.Object2");
//Objects must be the same length
if (ilist1.Count != ilist2.Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = ilist1.Count.ToString(CultureInfo.InvariantCulture),
Object2Value = ilist2.Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = ilist1,
Object2 = ilist2
};
AddDifference(parms.Result, difference);
if (parms.Result.ExceededDifferences)
return true;
}
return false;
}
private void CompareItems(CompareParms parms)
{
int count = 0;
int differenceCount = 0;
IEnumerator enumerator1 = ((IList) parms.Object1).GetEnumerator();
IEnumerator enumerator2 = ((IList) parms.Object2).GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
byte? b1 = enumerator1.Current as byte?;
byte? b2 = enumerator2.Current as byte?;
if (b1 != b2)
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count);
Difference difference = new Difference
{
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
PropertyName = currentBreadCrumb,
Object1Value = NiceString(b1),
Object2Value = NiceString(b2),
Object1 = b1,
Object2 = b2
};
AddDifference(parms.Result, difference);
differenceCount++;
if (differenceCount >= parms.Result.Config.MaxByteArrayDifferences)
return;
}
count++;
}
}
}
}

View File

@@ -1,80 +0,0 @@
using System;
using System.Collections;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two objects of type class
/// </summary>
public class ClassComparer : BaseTypeComparer
{
private readonly PropertyComparer _propertyComparer;
private readonly FieldComparer _fieldComparer;
/// <summary>
/// Constructor for the class comparer
/// </summary>
/// <param name="rootComparer">The root comparer instantiated by the RootComparerFactory</param>
public ClassComparer(RootComparer rootComparer) : base(rootComparer)
{
_propertyComparer = new PropertyComparer(rootComparer);
_fieldComparer = new FieldComparer(rootComparer);
}
/// <summary>
/// Returns true if the both objects are a class
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return (TypeHelper.IsClass(type1) && TypeHelper.IsClass(type2))
|| (TypeHelper.IsInterface(type1) && TypeHelper.IsInterface(type2));
}
/// <summary>
/// Compare two classes
/// </summary>
public override void CompareType(CompareParms parms)
{
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
//Custom classes that implement IEnumerable may have the same hash code
//Ignore objects with the same hash code
if (!(parms.Object1 is IEnumerable)
&& ReferenceEquals(parms.Object1, parms.Object2))
{
return;
}
Type t1 = parms.Object1.GetType();
Type t2 = parms.Object2.GetType();
//Check if the class type should be excluded based on the configuration
if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2))
return;
parms.Object1Type = t1;
parms.Object2Type = t2;
//Compare the properties
if (parms.Config.CompareProperties)
_propertyComparer.PerformCompareProperties(parms);
//Compare the fields
if (parms.Config.CompareFields)
_fieldComparer.PerformCompareFields(parms);
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
}
}

View File

@@ -1,134 +0,0 @@
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two collections of different types.
/// </summary>
public class CollectionComparer : BaseTypeComparer
{
/// <summary>
/// The main constructor.
/// </summary>
public CollectionComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are collections.
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return typeof(ICollection).IsAssignableFrom(type1) && typeof(ICollection).IsAssignableFrom(type2);
}
/// <summary>
/// Compare two collections.
/// </summary>
public override void CompareType(CompareParms parms)
{
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
Type t1 = parms.Object1.GetType();
Type t2 = parms.Object2.GetType();
//Check if the class type should be excluded based on the configuration
if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2))
return;
parms.Object1Type = t1;
parms.Object2Type = t2;
bool countsDifferent = CollectionsDifferentCount(parms);
if (parms.Result.ExceededDifferences)
return;
if (parms.Config.IgnoreCollectionOrder)
{
IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer);
logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent);
}
else
{
CompareItems(parms);
}
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
private void CompareItems(CompareParms parms)
{
int count = 0;
IEnumerator enumerator1 = ((ICollection)parms.Object1).GetEnumerator();
IEnumerator enumerator2 = ((ICollection)parms.Object2).GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Current,
Object2 = enumerator2.Current,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
count++;
}
}
private bool CollectionsDifferentCount(CompareParms parms)
{
//Get count by reflection since we can't cast it to HashSet<>
int count1 = ((ICollection)parms.Object1).Count;
int count2 = ((ICollection)parms.Object2).Count;
//Objects must be the same length
if (count1 != count2)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = count1.ToString(CultureInfo.InvariantCulture),
Object2Value = count2.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return true;
}
return false;
}
}
}

View File

@@ -1,70 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two generic objects
/// </summary>
/// <typeparam name="T1">The type of the first object</typeparam>
/// <typeparam name="T2">The type of the second object</typeparam>
public class CustomComparer<T1, T2> : BaseTypeComparer
{
/// <summary>
/// Method to evaluate the results, return true if two objects are equal
/// </summary>
public Func<T1, T2, bool> Compare = (t1, t2) => false;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public CustomComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Constructor that takes a default root comparer
/// </summary>
public CustomComparer() : this(RootComparerFactory.GetRootComparer())
{
}
/// <summary>
/// Constructor that takes a the predication with a default root comparer
/// </summary>
/// <param name="compare">A function to determine if two objects are equal</param>
public CustomComparer(Func<T1, T2, bool> compare) : this(RootComparerFactory.GetRootComparer(), compare)
{
}
/// <summary>
/// Constructor that takes a the predication with a root comparer
/// </summary>
/// <param name="rootComparer">The root comparer</param>
/// <param name="compare">Method to determine if two objects are equal</param>
public CustomComparer(RootComparer rootComparer, Func<T1, T2, bool> compare) : this(rootComparer)
{
Compare = compare;
}
/// <summary>
/// Compare two objects
/// </summary>
public override void CompareType(CompareParms parms)
{
if (!Compare((T1)parms.Object1, (T2)parms.Object2))
{
AddDifference(parms);
}
}
/// <summary>
/// Returns true if both objects match their types
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return type1 == typeof(T1) && type2 == typeof(T2);
}
}
}

View File

@@ -1,86 +0,0 @@
#if !NETSTANDARD
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare a data column
/// </summary>
public class DataColumnComparer : BaseTypeComparer
{
/// <summary>
/// Default constructor
/// </summary>
/// <param name="rootComparer"></param>
public DataColumnComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types compared are a DataColumn
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDataColumn(type1) && TypeHelper.IsDataColumn(type2);
}
/// <summary>
/// Compare a Data Column
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
DataColumn col1 = (DataColumn)parms.Object1;
DataColumn col2 = (DataColumn)parms.Object2;
CompareProp(parms, col1, col2, col1.AllowDBNull, col2.AllowDBNull, "AllowDBNull");
CompareProp(parms, col1, col2, col1.AutoIncrement, col2.AutoIncrement, "AutoIncrement");
CompareProp(parms, col1, col2, col1.AutoIncrementSeed, col2.AutoIncrementSeed, "AutoIncrementSeed");
CompareProp(parms, col1, col2, col1.AutoIncrementStep, col2.AutoIncrementStep, "AutoIncrementStep");
CompareProp(parms, col1, col2, col1.Caption, col2.Caption, "Caption");
CompareProp(parms, col1, col2, col1.ColumnName, col2.ColumnName, "ColumnName");
CompareProp(parms, col1, col2, col1.DataType, col2.DataType, "DataType");
CompareProp(parms, col1, col2, col1.DefaultValue, col2.DefaultValue, "DefaultValue");
CompareProp(parms, col1, col2, col1.Expression, col2.Expression, "Expression");
CompareProp(parms, col1, col2, col1.MaxLength, col2.MaxLength, "MaxLength");
CompareProp(parms, col1, col2, col1.Namespace, col2.Namespace, "Namespace");
CompareProp(parms, col1, col2, col1.Ordinal, col2.Ordinal, "Ordinal");
CompareProp(parms, col1, col2, col1.Prefix, col2.Prefix, "Prefix");
CompareProp(parms, col1, col2, col1.ReadOnly, col2.ReadOnly, "ReadOnly");
CompareProp(parms, col1, col2, col1.Unique, col2.Unique, "Unique");
}
private void CompareProp<T>(CompareParms parms, DataColumn col1, DataColumn col2, T prop1, T prop2, string propName)
{
if (parms.Result.ExceededDifferences)
return;
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, propName);
CompareParms childParms = new CompareParms();
childParms.Result = parms.Result;
childParms.Config = parms.Config;
childParms.BreadCrumb = currentBreadCrumb;
childParms.ParentObject1 = col1;
childParms.ParentObject2 = col2;
childParms.Object1 = prop1;
childParms.Object2 = prop2;
RootComparer.Compare(childParms);
}
}
}
#endif

View File

@@ -1,135 +0,0 @@
#if !NETSTANDARD
using System;
using System.Data;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare all columns in a data row
/// </summary>
public class DataRowComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DataRowComparer(RootComparer rootComparer)
: base(rootComparer)
{ }
/// <summary>
/// Returns true if this is a DataRow
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDataRow(type1) && TypeHelper.IsDataRow(type2);
}
/// <summary>
/// Compare two data rows
/// </summary>
public override void CompareType(CompareParms parms)
{
DataRow dataRow1 = parms.Object1 as DataRow;
DataRow dataRow2 = parms.Object2 as DataRow;
//This should never happen, null check happens one level up
if (dataRow1 == null || dataRow2 == null)
return;
for (int i = 0; i < Math.Min(dataRow2.Table.Columns.Count, dataRow1.Table.Columns.Count); i++)
{
//Only compare specific column names
if (parms.Config.MembersToIncludeSet.Count > 0 && !parms.Config.MembersToIncludeSet.Contains(dataRow1.Table.Columns[i].ColumnName))
continue;
//If we should ignore it, skip it
if (parms.Config.MembersToIncludeSet.Count == 0 && parms.Config.MembersToIgnoreSet.Contains(dataRow1.Table.Columns[i].ColumnName))
continue;
//If we should ignore read only, skip it
if (!parms.Config.CompareReadOnly && dataRow1.Table.Columns[i].ReadOnly)
continue;
//Both are null
if (dataRow1.IsNull(i) && dataRow2.IsNull(i))
continue;
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, dataRow1.Table.Columns[i].ColumnName);
//Check if one of them is null
if (dataRow1.IsNull(i))
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = currentBreadCrumb,
Object1Value = "(null)",
Object2Value = NiceString(dataRow2[i]),
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return;
}
if (dataRow2.IsNull(i))
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = currentBreadCrumb,
Object1Value = NiceString(dataRow1[i]),
Object2Value = "(null)",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return;
}
//Check if one of them is deleted
if (dataRow1.RowState == DataRowState.Deleted ^ dataRow2.RowState == DataRowState.Deleted)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = currentBreadCrumb,
Object1Value = dataRow1.RowState.ToString(),
Object2Value = dataRow2.RowState.ToString(),
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return;
}
CompareParms childParms = new CompareParms();
childParms.Result = parms.Result;
childParms.Config = parms.Config;
childParms.ParentObject1 = parms.Object1;
childParms.ParentObject2 = parms.Object2;
childParms.Object1 = dataRow1[i];
childParms.Object2 = dataRow2[i];
childParms.BreadCrumb = currentBreadCrumb;
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
}
}
#endif

View File

@@ -1,173 +0,0 @@
#if !NETSTANDARD
using System;
using System.Data;
using System.Globalization;
using System.Linq;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare all rows in a data table
/// </summary>
public class DataTableComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DataTableComparer(RootComparer rootComparer)
: base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are of type DataTable
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDataTable(type1) && TypeHelper.IsDataTable(type2);
}
/// <summary>
/// Compare two datatables
/// </summary>
public override void CompareType(CompareParms parms)
{
DataTable dataTable1 = parms.Object1 as DataTable;
DataTable dataTable2 = parms.Object2 as DataTable;
//This should never happen, null check happens one level up
if (dataTable1 == null || dataTable2 == null)
return;
//Only compare specific table names
if (parms.Config.MembersToInclude.Count > 0 && !parms.Config.MembersToInclude.Contains(dataTable1.TableName))
return;
//If we should ignore it, skip it
if (parms.Config.MembersToInclude.Count == 0 && parms.Config.MembersToIgnore.Contains(dataTable1.TableName))
return;
//There must be the same amount of rows in the datatable
if (dataTable1.Rows.Count != dataTable2.Rows.Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = dataTable1.Rows.Count.ToString(CultureInfo.InvariantCulture),
Object2Value = dataTable2.Rows.Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Rows.Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
if (parms.Result.ExceededDifferences)
return;
}
if (ColumnsDifferent(parms)) return;
CompareEachRow(parms);
}
private bool ColumnsDifferent(CompareParms parms)
{
DataTable dataTable1 = parms.Object1 as DataTable;
DataTable dataTable2 = parms.Object2 as DataTable;
if (dataTable1 == null)
throw new ArgumentException("parms.Object1");
if (dataTable2 == null)
throw new ArgumentException("parms.Object2");
if (dataTable1.Columns.Count != dataTable2.Columns.Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = dataTable1.Columns.Count.ToString(CultureInfo.InvariantCulture),
Object2Value = dataTable2.Columns.Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Columns.Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
if (parms.Result.ExceededDifferences)
return true;
}
foreach (var i in Enumerable.Range(0, dataTable1.Columns.Count))
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Columns", string.Empty, i);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = dataTable1.Columns[i],
Object2 = dataTable2.Columns[i],
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return true;
}
return false;
}
private void CompareEachRow(CompareParms parms)
{
DataTable dataTable1 = parms.Object1 as DataTable;
DataTable dataTable2 = parms.Object2 as DataTable;
if (dataTable1 == null)
throw new ArgumentException("parms.Object1");
if (dataTable2 == null)
throw new ArgumentException("parms.Object2");
for (int i = 0; i < Math.Min(dataTable1.Rows.Count, dataTable2.Rows.Count); i++)
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Rows", string.Empty, i);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = dataTable1.Rows[i],
Object2 = dataTable2.Rows[i],
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
}
}
#endif

View File

@@ -1,101 +0,0 @@
#if !NETSTANDARD
using System;
using System.Data;
using System.Globalization;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare all tables and all rows in all tables
/// </summary>
public class DatasetComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DatasetComparer(RootComparer rootComparer)
: base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are data sets
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDataset(type1) && TypeHelper.IsDataset(type2);
}
/// <summary>
/// Compare two data sets
/// </summary>
public override void CompareType(CompareParms parms)
{
DataSet dataSet1 = parms.Object1 as DataSet;
DataSet dataSet2 = parms.Object2 as DataSet;
//This should never happen, null check happens one level up
if (dataSet1 == null || dataSet2 == null)
return;
if (TableCountsDifferent(parms, dataSet2, dataSet1)) return;
CompareEachTable(parms, dataSet1, dataSet2);
}
private bool TableCountsDifferent(CompareParms parms, DataSet dataSet2, DataSet dataSet1)
{
if (dataSet1.Tables.Count != dataSet2.Tables.Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = dataSet1.Tables.Count.ToString(CultureInfo.InvariantCulture),
Object2Value = dataSet2.Tables.Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Tables.Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
if (parms.Result.ExceededDifferences)
return true;
}
return false;
}
private void CompareEachTable(CompareParms parms, DataSet dataSet1, DataSet dataSet2)
{
for (int i = 0; i < Math.Min(dataSet1.Tables.Count, dataSet2.Tables.Count); i++)
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Tables", string.Empty,
dataSet1.Tables[i].TableName);
CompareParms childParms = new CompareParms();
childParms.Result = parms.Result;
childParms.Config = parms.Config;
childParms.BreadCrumb = currentBreadCrumb;
childParms.ParentObject1 = dataSet1;
childParms.ParentObject2 = dataSet2;
childParms.Object1 = dataSet1.Tables[i];
childParms.Object2 = dataSet2.Tables[i];
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
}
}
#endif

View File

@@ -1,53 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare dates with the option to ignore based on milliseconds
/// </summary>
public class DateComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DateComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are DateTime
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDateTime(type1) && TypeHelper.IsDateTime(type2);
}
/// <summary>
/// Compare two DateTime variables
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
DateTime date1 = (DateTime) parms.Object1;
DateTime date2 = (DateTime) parms.Object2;
if (date1.Kind != date2.Kind)
{
date1 = date1.ToUniversalTime();
date2 = date2.ToUniversalTime();
}
if (Math.Abs(date1.Subtract(date2).TotalMilliseconds) > parms.Config.MaxMillisecondsDateDifference)
AddDifference(parms);
}
}
}

View File

@@ -1,68 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare DateTimeOffsets with the ability to ignore millisecond differences
/// </summary>
public class DateTimeOffSetComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DateTimeOffSetComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are DateTimeOffset
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDateTimeOffset(type1) && TypeHelper.IsDateTimeOffset(type2);
}
/// <summary>
/// Compare two DateTimeOffset
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
DateTimeOffset date1 = (DateTimeOffset)parms.Object1;
DateTimeOffset date2 = (DateTimeOffset)parms.Object2;
if (parms.Config.IgnoreDateTimeOffsetTimezones)
{
date1 = date1.ToUniversalTime();
date2 = date2.ToUniversalTime();
}
if (parms.Config.CompareDateTimeOffsetWithOffsets
&& Math.Abs((date1 - date2).TotalMilliseconds) > parms.Config.MaxMillisecondsDateDifference)
{
AddDifference(parms);
}
else if (!parms.Config.CompareDateTimeOffsetWithOffsets)
{
DateTime date1NoOffset = new DateTime(date1.Year, date1.Month, date1.Day, date1.Hour, date1.Minute, date1.Second);
date1NoOffset = date1NoOffset.AddMilliseconds(date1.Millisecond);
DateTime date2NoOffset = new DateTime(date2.Year, date2.Month, date2.Day, date2.Hour, date2.Minute, date2.Second);
date2NoOffset = date2NoOffset.AddMilliseconds(date2.Millisecond);
if (Math.Abs(date1NoOffset.Subtract(date2NoOffset).TotalMilliseconds) > parms.Config.MaxMillisecondsDateDifference)
{
AddDifference(parms);
}
}
}
}
}

View File

@@ -1,46 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare Decimal values with the ability to specify the precision
/// </summary>
public class DecimalComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DecimalComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are double
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDecimal(type1) && TypeHelper.IsDecimal(type2);
}
/// <summary>
/// Compare two decimals
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
decimal decimal1 = (decimal)parms.Object1;
decimal decimal2 = (decimal)parms.Object2;
if (Math.Abs(decimal1 - decimal2) > parms.Config.DecimalPrecision)
AddDifference(parms);
}
}
}

View File

@@ -1,236 +0,0 @@
using System;
using System.Collections;
using System.Globalization;
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two dictionaries
/// </summary>
public class DictionaryComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DictionaryComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are dictionaries
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return ((TypeHelper.IsIDictionary(type1) || type1 == null) &&
(TypeHelper.IsIDictionary(type2) || type2 == null) &&
!(type1 == null && type2 == null));
}
/// <summary>
/// Compare two dictionaries
/// </summary>
public override void CompareType(CompareParms parms)
{
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
//Objects must be the same length
bool countsDifferent = DictionaryCountsDifferent(parms);
if (countsDifferent && parms.Result.ExceededDifferences)
return;
bool shouldCompareByKeys = ShouldCompareByKeys(parms);
if (shouldCompareByKeys)
{
CompareByKeys(parms);
}
else
{
if (parms.Config.IgnoreCollectionOrder)
{
IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer);
logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent);
}
else
{
CompareByEnumerator(parms);
}
}
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
/// <summary>
/// This is to handle funky situation of having a complex object as a key
/// (In this case a dictionary as a key)
/// https://github.com/GregFinzer/Compare-Net-Objects/issues/222
/// </summary>
/// <param name="parms"></param>
/// <returns></returns>
private static bool ShouldCompareByKeys(CompareParms parms)
{
bool shouldCompareByKeys = true;
if (parms.Object1 != null)
{
var dict1 = ((IDictionary) parms.Object1);
if (dict1.Keys.Count > 0)
{
var enumerator1 = ((IDictionary) parms.Object1).GetEnumerator();
enumerator1.MoveNext();
shouldCompareByKeys =
enumerator1.Key != null && TypeHelper.IsSimpleType(enumerator1.Key.GetType());
}
}
return shouldCompareByKeys;
}
private void CompareByKeys(CompareParms parms)
{
var dict1 = ((IDictionary)parms.Object1);
var dict2 = ((IDictionary)parms.Object2);
if (dict1 != null)
{
foreach (var key in dict1.Keys)
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "[" +key.ToString()+ "].Value");
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = dict1[key],
Object2 = (dict2 != null) && dict2.Contains(key) ? dict2[key] : null,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
if (dict2 != null)
{
foreach (var key in dict2.Keys)
{
if (dict1 != null && dict1.Contains(key))
continue;
var currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb,
"[" + key.ToString() + "].Value");
var childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = null,
Object2 = dict2[key],
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
}
private void CompareByEnumerator(CompareParms parms)
{
var enumerator1 = ((IDictionary)parms.Object1).GetEnumerator();
var enumerator2 = ((IDictionary)parms.Object2).GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Key");
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Key,
Object2 = enumerator2.Key,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Value");
childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Value,
Object2 = enumerator2.Value,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
private bool DictionaryCountsDifferent(CompareParms parms)
{
IDictionary iDict1 = parms.Object1 as IDictionary;
IDictionary iDict2 = parms.Object2 as IDictionary;
int iDict1Count = (iDict1 == null) ? 0 : iDict1.Count;
int iDict2Count = (iDict2 == null) ? 0 : iDict2.Count;
if (iDict1Count == iDict2Count)
return false;
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = iDict1Count.ToString(CultureInfo.InvariantCulture),
Object2Value = iDict2Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = iDict1,
Object2 = iDict2
};
AddDifference(parms.Result, difference);
return true;
}
}
}

View File

@@ -1,50 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare Double values with the ability to specify the precision
/// </summary>
public class DoubleComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public DoubleComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are double
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsDouble(type1) && TypeHelper.IsDouble(type2);
}
/// <summary>
/// Compare two doubles
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
Double double1 = (Double)parms.Object1;
Double double2 = (Double)parms.Object2;
double diff = double1 - double2;
if ((Math.Abs(diff) > parms.Config.DoublePrecision)
|| (Double.IsNaN(diff) && (!Double.IsNaN(double1) || !Double.IsNaN(double2))))
{
AddDifference(parms);
}
}
}
}

View File

@@ -1,40 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare to enum values
/// </summary>
public class EnumComparer : BaseTypeComparer
{
/// <summary>
/// Constructor with a default root comparer
/// </summary>
/// <param name="rootComparer"></param>
public EnumComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are of type enum
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsEnum(type1) && TypeHelper.IsEnum(type2);
}
/// <summary>
/// Compare two enums
/// </summary>
public override void CompareType(CompareParms parms)
{
if (parms.Object1.ToString() != parms.Object2.ToString())
{
AddDifference(parms);
}
}
}
}

View File

@@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two LINQ enumerators
/// </summary>
public class EnumerableComparer :BaseTypeComparer
{
private readonly ListComparer _compareIList;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public EnumerableComparer(RootComparer rootComparer) : base(rootComparer)
{
_compareIList = new ListComparer(rootComparer);
}
/// <summary>
/// Returns true if either object is of type LINQ Enumerator
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
if (type1 == null || type2 == null)
return false;
return TypeHelper.IsEnumerable(type1) || TypeHelper.IsEnumerable(type2);
}
/// <summary>
/// Compare two objects that implement LINQ Enumerator
/// </summary>
public override void CompareType(CompareParms parms)
{
var oldObject1 = parms.Object1;
var oldObject2 = parms.Object2;
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
Type t1 = parms.Object1.GetType();
Type t2 = parms.Object2.GetType();
var l1 = TypeHelper.IsEnumerable(t1) ? ConvertEnumerableToList(parms.Object1) : parms.Object1;
var l2 = TypeHelper.IsEnumerable(t2) ? ConvertEnumerableToList(parms.Object2) : parms.Object2;
parms.Object1 = l1;
parms.Object2 = l2;
_compareIList.CompareType(parms);
}
finally
{
parms.Result.RemoveParent(oldObject1);
parms.Result.RemoveParent(oldObject2);
}
}
private object ConvertEnumerableToList(object source)
{
var type = source.GetType();
if (type.IsArray)
return source;
Type enumerableGenArg = null;
foreach (var inter in type.GetInterfaces())
{
if (inter.GetTypeInfo().IsGenericType && inter.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
enumerableGenArg = inter.GetGenericArguments()[0];
break;
}
}
if (enumerableGenArg == null)
{
#if NETSTANDARD
throw new Exception("Cannot get IEnumerable definition");
#else
throw new ApplicationException("Cannot get IEnumerable definition");
#endif
}
MethodInfo toList = typeof(Enumerable).GetMethod("ToList");
MethodInfo constructedToList = toList.MakeGenericMethod(enumerableGenArg);
object resultList = constructedToList.Invoke(null, new[] { source });
return resultList;
}
}
}

View File

@@ -1,131 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare all the fields of a class or struct (Note: inherits from BaseComparer instead of TypeComparer).
/// </summary>
public class FieldComparer : BaseComparer
{
private readonly RootComparer _rootComparer;
/// <summary>
/// Constructor with a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public FieldComparer(RootComparer rootComparer)
{
_rootComparer = rootComparer;
}
/// <summary>
/// Compare the fields of a class
/// </summary>
public void PerformCompareFields(CompareParms parms)
{
var currentFields = GetCurrentFields(parms);
foreach (FieldInfo item in currentFields)
{
CompareField(parms, item);
if (parms.Result.ExceededDifferences)
return;
}
}
private void CompareField(CompareParms parms, FieldInfo item)
{
//Skip if this is a shallow compare
if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(item.FieldType))
return;
//Skip if it should be excluded based on the configuration
if (ExcludeLogic.ShouldExcludeMember(parms.Config, item, parms.Object1Type))
return;
//If we ignore types then we must get correct FieldInfo object
FieldInfo secondFieldInfo = GetSecondFieldInfo(parms, item);
//If the field does not exist, and we are ignoring the object types, skip it - unless we have set IgnoreMissingFields = true
if ((parms.Config.IgnoreObjectTypes || parms.Config.IgnoreConcreteTypes) && secondFieldInfo == null && parms.Config.IgnoreMissingFields)
return;
object objectValue1 = item.GetValue(parms.Object1);
object objectValue2 = secondFieldInfo != null ? secondFieldInfo.GetValue(parms.Object2) : null;
bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.IsParent(objectValue1));
bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.IsParent(objectValue2));
//Skip fields that point to the parent
if ((TypeHelper.IsClass(item.FieldType) || TypeHelper.IsInterface(item.FieldType))
&& (object1IsParent || object2IsParent))
{
return;
}
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, item.Name);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = objectValue1,
Object2 = objectValue2,
BreadCrumb = currentBreadCrumb
};
_rootComparer.Compare(childParms);
}
private static FieldInfo GetSecondFieldInfo(CompareParms parms, FieldInfo item)
{
FieldInfo secondFieldInfo = null;
if (parms.Config.IgnoreObjectTypes || parms.Config.IgnoreConcreteTypes)
{
IEnumerable<FieldInfo> secondObjectFieldInfos = Cache.GetFieldInfo(parms.Config, parms.Object2Type);
foreach (var fieldInfo in secondObjectFieldInfos)
{
if (fieldInfo.Name != item.Name) continue;
secondFieldInfo = fieldInfo;
break;
}
}
else
secondFieldInfo = item;
return secondFieldInfo;
}
private static IEnumerable<FieldInfo> GetCurrentFields(CompareParms parms)
{
IEnumerable<FieldInfo> currentFields = null;
//Interface Member Logic
if (parms.Config.InterfaceMembers.Count > 0)
{
Type[] interfaces = parms.Object1Type.GetInterfaces();
foreach (var type in parms.Config.InterfaceMembers)
{
if (interfaces.Contains(type))
{
currentFields = Cache.GetFieldInfo(parms.Config, type);
break;
}
}
}
if (currentFields == null)
currentFields = Cache.GetFieldInfo(parms.Config, parms.Object1Type);
return currentFields;
}
}
}

View File

@@ -1,78 +0,0 @@
#if !NETSTANDARD
using System;
using System.Drawing;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Class FontDescriptorComparer.
/// </summary>
public class FontComparer : BaseTypeComparer
{
/// <summary>
/// Protected constructor that references the root comparer
/// </summary>
/// <param name="rootComparer">The root comparer.</param>
public FontComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// If true the type comparer will handle the comparison for the type
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns><c>true</c> if [is type match] [the specified type1]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.NotImplementedException"></exception>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsFont(type1) && TypeHelper.IsFont(type2);
}
/// <summary>
/// Compare the two fonts
/// </summary>
public override void CompareType(CompareParms parms)
{
Font font1 = parms.Object1 as Font;
Font font2 = parms.Object2 as Font;
if (font1 == null || font2 == null)
return;
CompareProp(parms, font1.Bold, font2.Bold, "Bold");
CompareProp(parms, font1.FontFamily.Name, font2.FontFamily.Name, "FontFamily.Name");
CompareProp(parms, font1.OriginalFontName, font2.OriginalFontName, "OriginalFontName");
CompareProp(parms, font1.Size, font2.Size, "Size");
CompareProp(parms, font1.SizeInPoints, font2.SizeInPoints, "SizeInPoints");
CompareProp(parms, font1.Strikeout, font2.Strikeout, "Strikeout");
CompareProp(parms, font1.Style, font2.Style, "Style");
CompareProp(parms, font1.SystemFontName, font2.SystemFontName, "SystemFontName");
CompareProp(parms, font1.Underline, font2.Underline, "Underline");
CompareProp(parms, font1.Unit, font2.Unit, "Unit");
}
private void CompareProp(CompareParms parms, object prop1, object prop2, string propName)
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, propName);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = prop1,
Object2 = prop2,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
}
}
}
#endif

View File

@@ -1,135 +0,0 @@
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two hash sets
/// </summary>
public class HashSetComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public HashSetComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are hash sets
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsHashSet(type1) && TypeHelper.IsHashSet(type2);
}
/// <summary>
/// Compare two hash sets
/// </summary>
public override void CompareType(CompareParms parms)
{
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
Type t1 = parms.Object1.GetType();
parms.Object1Type = t1;
Type t2 = parms.Object2.GetType();
parms.Object2Type = t2;
bool countsDifferent = HashSetsDifferentCount(parms);
if (parms.Result.ExceededDifferences)
return;
if (parms.Config.IgnoreCollectionOrder)
{
IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer);
logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent);
}
else
{
CompareItems(parms);
}
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
private void CompareItems(CompareParms parms)
{
int count = 0;
//Get enumerators by reflection
MethodInfo method1Info = Cache.GetMethod(parms.Object1Type, "GetEnumerator");
IEnumerator enumerator1 = (IEnumerator)method1Info.Invoke(parms.Object1, null);
MethodInfo method2Info = Cache.GetMethod(parms.Object2Type, "GetEnumerator");
IEnumerator enumerator2 = (IEnumerator) method2Info.Invoke(parms.Object2, null);
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Current,
Object2 = enumerator2.Current,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
count++;
}
}
private bool HashSetsDifferentCount(CompareParms parms)
{
//Get count by reflection since we can't cast it to HashSet<>
int hashSet1Count = (int) Cache.GetPropertyValue(parms.Result.Config, parms.Object1Type, parms.Object1, "Count");
int hashSet2Count = (int)Cache.GetPropertyValue(parms.Result.Config, parms.Object2Type, parms.Object2, "Count");
//Objects must be the same length
if (hashSet1Count != hashSet2Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = hashSet1Count.ToString(CultureInfo.InvariantCulture),
Object2Value = hashSet2Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return true;
}
return false;
}
}
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections;
using System.Linq;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compares System.Collections.Immutable.ImmutableArray
/// </summary>
public class ImmutableArrayComparer : BaseTypeComparer
{
private readonly ListComparer _listComparer;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public ImmutableArrayComparer(RootComparer rootComparer) : base(rootComparer)
{
_listComparer = new ListComparer(rootComparer);
}
/// <summary>
/// Returns true if both are immutable arrays with same generic argument type
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsImmutableArray(type1) && TypeHelper.IsImmutableArray(type2);
}
/// <summary>
/// Compares two immutable arrays
/// </summary>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
parms.Object1 = ((IList) parms.Object1).Cast<object>().ToArray();
parms.Object2 = ((IList) parms.Object2).Cast<object>().ToArray();
_listComparer.CompareType(parms);
}
}
}

View File

@@ -1,153 +0,0 @@
using System;
using System.Globalization;
using System.Reflection;
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare an integer indexer (Note, inherits from BaseComparer, not TypeComparer)
/// </summary>
public class IndexerComparer : BaseComparer
{
private readonly RootComparer _rootComparer;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public IndexerComparer(RootComparer rootComparer)
{
_rootComparer = rootComparer;
}
/// <summary>
/// Compare an integer indexer
/// </summary>
public void CompareIndexer(CompareParms parms, PropertyEntity info, PropertyEntity secondObjectInfo)
{
if (info == null)
throw new ArgumentNullException("info");
#if !NETSTANDARD
var type = info.ReflectedType;
var type2 = secondObjectInfo.ReflectedType;
#else
var type = info.DeclaringType;
var type2 = secondObjectInfo.DeclaringType;
#endif
if (type == null || type2 == null)
throw new ArgumentNullException("info");
int indexerCount1 = (int)type.GetProperty("Count").GetGetMethod().Invoke(parms.Object1, new object[] { });
int indexerCount2 = (int)type2.GetProperty("Count").GetGetMethod().Invoke(parms.Object2, new object[] { });
bool differentCounts = IndexersHaveDifferentLength(parms, info, indexerCount1, indexerCount2);
if (parms.Result.ExceededDifferences)
return;
if (parms.Config.IgnoreCollectionOrder)
{
var enumerable1 = new IndexerCollectionLooper(parms.Object1, info.PropertyInfo, indexerCount1);
var enumerable2 = new IndexerCollectionLooper(parms.Object2, secondObjectInfo.PropertyInfo, indexerCount2);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerable1,
Object2 = enumerable2,
BreadCrumb = parms.BreadCrumb
};
IgnoreOrderLogic logic = new IgnoreOrderLogic(_rootComparer);
logic.CompareEnumeratorIgnoreOrder(childParms, differentCounts);
}
else
{
string currentCrumb;
// Run on indexer
for (int i = 0; i < indexerCount1; i++)
{
currentCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name, string.Empty, i);
object objectValue1 = info.PropertyInfo.GetValue(parms.Object1, new object[] { i });
object objectValue2 = null;
if (i < indexerCount2)
objectValue2 = secondObjectInfo.PropertyInfo.GetValue(parms.Object2, new object[] { i });
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = objectValue1,
Object2 = objectValue2,
BreadCrumb = currentCrumb
};
_rootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
if (indexerCount1 < indexerCount2)
{
for (int j = indexerCount1; j < indexerCount2; j++)
{
currentCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name, string.Empty, j);
object objectValue2 = secondObjectInfo.PropertyInfo.GetValue(parms.Object2, new object[] { j });
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = null,
Object2 = objectValue2,
BreadCrumb = currentCrumb
};
_rootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
}
}
}
}
private bool IndexersHaveDifferentLength(CompareParms parms, PropertyEntity info, int indexerCount1, int indexerCount2)
{
if (info == null)
throw new ArgumentNullException("info");
if (indexerCount1 != indexerCount2)
{
string currentCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name);
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = currentCrumb,
Object1Value = indexerCount1.ToString(CultureInfo.InvariantCulture),
Object2Value = indexerCount2.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return true;
}
return false;
}
}
}

View File

@@ -1,97 +0,0 @@
#if NETFULL || NETSTANDARD2_0 || NETSTANDARD2_1
using System;
using System.Globalization;
using System.Net;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two IP End Points
/// </summary>
public class IpEndPointComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public IpEndPointComparer(RootComparer rootComparer)
: base(rootComparer)
{}
/// <summary>
/// Returns true if both objects are an IP End Point
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsIpEndPoint(type1) && TypeHelper.IsIpEndPoint(type2);
}
/// <summary>
/// Compare two IP End Points
/// </summary>
public override void CompareType(CompareParms parms)
{
IPEndPoint ipEndPoint1 = parms.Object1 as IPEndPoint;
IPEndPoint ipEndPoint2 = parms.Object2 as IPEndPoint;
//Null check happens above
if (ipEndPoint1 == null || ipEndPoint2 == null)
return;
ComparePort(parms, ipEndPoint1, ipEndPoint2);
if (parms.Result.ExceededDifferences)
return;
CompareAddress(parms, ipEndPoint1, ipEndPoint2);
}
private void ComparePort(CompareParms parms, IPEndPoint ipEndPoint1, IPEndPoint ipEndPoint2)
{
if (ipEndPoint1.Port != ipEndPoint2.Port)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = ipEndPoint1.Port.ToString(CultureInfo.InvariantCulture),
Object2Value = ipEndPoint2.Port.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Port",
Object1 = ipEndPoint1,
Object2 = ipEndPoint2
};
AddDifference(parms.Result, difference);
}
}
private void CompareAddress(CompareParms parms, IPEndPoint ipEndPoint1, IPEndPoint ipEndPoint2)
{
if (ipEndPoint1.Address.ToString() != ipEndPoint2.Address.ToString())
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = ipEndPoint1.Address.ToString(),
Object2Value = ipEndPoint2.Address.ToString(),
ChildPropertyName = "Address",
Object1 = ipEndPoint1,
Object2 = ipEndPoint2
};
AddDifference(parms.Result, difference);
}
}
}
}
#endif

View File

@@ -1,228 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare objects that implement IList
/// </summary>
public class ListComparer : BaseTypeComparer
{
private readonly PropertyComparer _propertyComparer;
private readonly FieldComparer _fieldComparer;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public ListComparer(RootComparer rootComparer) : base(rootComparer)
{
_propertyComparer = new PropertyComparer(rootComparer);
_fieldComparer = new FieldComparer(rootComparer);
}
/// <summary>
/// Returns true if both objects implement IList
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsIList(type1) && TypeHelper.IsIList(type2);
}
/// <summary>
/// Compare two objects that implement IList
/// </summary>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
Type t1 = parms.Object1.GetType();
Type t2 = parms.Object2.GetType();
//Check if the class type should be excluded based on the configuration
if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2))
return;
parms.Object1Type = t1;
parms.Object2Type = t2;
if (parms.Result.ExceededDifferences)
return;
bool countsDifferent = ListsHaveDifferentCounts(parms);
// If items is collections, need to use default compare logic, not ignore order logic.
// We cannot ignore order for nested collections because we will get an reflection exception.
// May be need to display some warning or write about this behavior in documentation.
if (parms.Config.IgnoreCollectionOrder && !ChildShouldBeComparedWithoutOrder(parms))
{
// TODO: allow IndexerComparer to works with types (now it works only with properties).
IgnoreOrderLogic ignoreOrderLogic = new IgnoreOrderLogic(RootComparer);
ignoreOrderLogic.CompareEnumeratorIgnoreOrder(parms, countsDifferent);
}
else
{
CompareItems(parms);
}
//Properties on the root of a collection
CompareProperties(parms);
CompareFields(parms);
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
private void CompareFields(CompareParms parms)
{
if (parms.Config.CompareFields)
{
_fieldComparer.PerformCompareFields(parms);
}
}
private void CompareProperties(CompareParms parms)
{
if (parms.Config.CompareProperties)
{
_propertyComparer.PerformCompareProperties(parms, true);
}
}
private bool ListsHaveDifferentCounts(CompareParms parms)
{
IList ilist1 = parms.Object1 as IList;
IList ilist2 = parms.Object2 as IList;
if (ilist1 == null)
throw new ArgumentException("parms.Object1");
if (ilist2 == null)
throw new ArgumentException("parms.Object2");
try
{
//Objects must be the same length
if (ilist1.Count != ilist2.Count)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = ilist1.Count.ToString(CultureInfo.InvariantCulture),
Object2Value = ilist2.Count.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = ilist1,
Object2 = ilist2
};
AddDifference(parms.Result, difference);
return true;
}
}
catch (ObjectDisposedException)
{
if (!parms.Config.IgnoreObjectDisposedException)
throw;
return true;
}
return false;
}
private bool ChildShouldBeComparedWithoutOrder(CompareParms parms)
{
IEnumerator enumerator1 = ((IEnumerable)parms.Object1).GetEnumerator();
// We should ensure that all items is enumerable, list or dictionary.
bool hasItems = false;
var results = new List<bool>();
while (enumerator1.MoveNext())
{
hasItems = true;
if (enumerator1.Current is null)
continue;
Type type = enumerator1.Current.GetType();
bool shouldCompareAndIgnoreOrder =
TypeHelper.IsEnumerable(type) ||
TypeHelper.IsIList(type) ||
TypeHelper.IsIDictionary(type);
results.Add(shouldCompareAndIgnoreOrder);
}
// Take into account that items can be objects with mixed types.
// Throw an exception that this case is unsupported.
if (hasItems && results.Count > 0)
{
bool firstResult = results[0];
if (results.Any(x => x != firstResult))
{
throw new NotSupportedException(
"Collection has nested collections and some other types. " +
"IgnoreCollectionOrder should be false for such cases."
);
}
return firstResult;
}
// If all items is null, we can compare as usual.
// Order does not change anything in this case.
return hasItems;
}
private void CompareItems(CompareParms parms)
{
int count = 0;
IEnumerator enumerator1 = ((IList) parms.Object1).GetEnumerator();
IEnumerator enumerator2 = ((IList) parms.Object2).GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Current,
Object2 = enumerator2.Current,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
count++;
}
}
}
}

View File

@@ -1,50 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare to pointers
/// </summary>
public class PointerComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public PointerComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are a pointer
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsPointer(type1) && TypeHelper.IsPointer(type2);
}
/// <summary>
/// Compare two pointers
/// </summary>
public override void CompareType(CompareParms parms)
{
if ((parms.Object1 is IntPtr && parms.Object2 is IntPtr && ((IntPtr)parms.Object1) != ((IntPtr)parms.Object2))
|| (parms.Object1 is UIntPtr && parms.Object2 is UIntPtr && ((UIntPtr)parms.Object1) != ((UIntPtr)parms.Object2)))
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
}
}
}
}

View File

@@ -1,338 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two properties (Note: inherits from BaseComparer instead of TypeComparer).
/// </summary>
public class PropertyComparer : BaseComparer
{
private readonly RootComparer _rootComparer;
private readonly IndexerComparer _indexerComparer;
private static readonly string[] _baseList = { "Count", "Capacity", "Item" };
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public PropertyComparer(RootComparer rootComparer)
{
_rootComparer = rootComparer;
_indexerComparer = new IndexerComparer(rootComparer);
}
/// <summary>
/// Compare the properties of a class
/// </summary>
public void PerformCompareProperties(CompareParms parms, bool ignoreBaseList= false)
{
List<PropertyEntity> object1Properties = GetCurrentProperties(parms, parms.Object1, parms.Object1Type);
List<PropertyEntity> object2Properties = GetCurrentProperties(parms, parms.Object2, parms.Object2Type);
foreach (PropertyEntity propertyEntity in object1Properties)
{
if (ignoreBaseList && _baseList.Contains(propertyEntity.Name))
continue;
CompareProperty(parms, propertyEntity, object2Properties);
if (parms.Result.ExceededDifferences)
return;
}
}
/// <summary>
/// Compare a single property of a class
/// </summary>
/// <param name="parms"></param>
/// <param name="info"></param>
/// <param name="object2Properties"></param>
private void CompareProperty(CompareParms parms, PropertyEntity info, List<PropertyEntity> object2Properties)
{
//If we can't read it, skip it
if (info.CanRead == false)
return;
//Skip if this is a shallow compare
if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(info.PropertyType))
return;
//Skip if it should be excluded based on the configuration
if (info.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, info.PropertyInfo, info.DeclaringType))
return;
//This is a dynamic property to be excluded on an expando object
if (info.IsDynamic && ExcludeLogic.ShouldExcludeDynamicMember(parms.Config, info.Name, info.DeclaringType))
return;
//If we should ignore read only, skip it
if (!parms.Config.CompareReadOnly && info.CanWrite == false)
return;
//If we ignore types then we must get correct PropertyInfo object
PropertyEntity secondObjectInfo = GetSecondObjectInfo(info, object2Properties);
//If the property does not exist, and we are ignoring the object types, skip it - unless we have set IgnoreMissingProperties = true
if ((parms.Config.IgnoreObjectTypes || parms.Config.IgnoreConcreteTypes) && secondObjectInfo == null && parms.Config.IgnoreMissingProperties)
return;
//Check if we have custom function to validate property
BaseTypeComparer customComparer = null;
if (info.PropertyInfo != null)
customComparer = CustomValidationLogic.CustomValidatorForMember(parms.Config, info.PropertyInfo, info.DeclaringType)
?? CustomValidationLogic.CustomValidatorForDynamicMember(parms.Config, info.Name, info.DeclaringType);
object objectValue1;
object objectValue2;
if (!IsValidIndexer(parms.Config, info, parms.BreadCrumb))
{
objectValue1 = info.Value;
objectValue2 = secondObjectInfo != null ? secondObjectInfo.Value : null;
}
else
{
_indexerComparer.CompareIndexer(parms, info, secondObjectInfo);
return;
}
bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.IsParent(objectValue1));
bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.IsParent(objectValue2));
//Skip properties where both point to the corresponding parent
if ((TypeHelper.IsClass(info.PropertyType) || TypeHelper.IsInterface(info.PropertyType) || TypeHelper.IsStruct(info.PropertyType))
&& (object1IsParent && object2IsParent))
{
return;
}
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = objectValue1,
Object2 = objectValue2,
BreadCrumb = currentBreadCrumb,
CustomPropertyComparer = customComparer,
Object1DeclaredType = info?.PropertyType,
Object2DeclaredType = secondObjectInfo?.PropertyType
};
_rootComparer.Compare(childParms);
}
private static PropertyEntity GetSecondObjectInfo(PropertyEntity info, List<PropertyEntity> object2Properties)
{
foreach (var object2Property in object2Properties)
{
if (info.Name == object2Property.Name)
return object2Property;
}
return null;
}
private static List<PropertyEntity> GetCurrentProperties(CompareParms parms, object objectValue, Type objectType)
{
return HandleDynamicObject(objectValue, objectType)
?? HandleInterfaceMembers(parms, objectValue, objectType)
?? HandleNormalProperties(parms, objectValue, objectType);
}
private static List<PropertyEntity> HandleNormalProperties(CompareParms parms, object objectValue, Type objectType)
{
IEnumerable<PropertyInfo> properties = Cache.GetPropertyInfo(parms.Result.Config, objectType);
return AddPropertyInfos(parms, objectValue, objectType, properties);
}
private static List<PropertyEntity> AddPropertyInfos(CompareParms parms,
object objectValue,
Type objectType,
IEnumerable<PropertyInfo> properties)
{
List<PropertyEntity> currentProperties = new List<PropertyEntity>();
foreach (var property in properties)
{
if (ExcludeLogic.ShouldExcludeMember(parms.Config, property, objectType))
continue;
if (!property.CanRead && !parms.Config.CompareReadOnly)
continue;
PropertyEntity propertyEntity = new PropertyEntity();
propertyEntity.IsDynamic = false;
propertyEntity.Name = property.Name;
propertyEntity.CanRead = property.CanRead;
propertyEntity.CanWrite = property.CanWrite;
propertyEntity.PropertyType = property.PropertyType;
#if !NETSTANDARD
propertyEntity.ReflectedType = property.ReflectedType;
#endif
propertyEntity.Indexers.AddRange(property.GetIndexParameters());
propertyEntity.DeclaringType = objectType;
if (propertyEntity.CanRead && (propertyEntity.Indexers.Count == 0))
{
try
{
propertyEntity.Value = property.GetValue(objectValue, null);
}
catch (System.Reflection.TargetInvocationException)
{
}
catch (System.NotSupportedException)
{
}
}
propertyEntity.PropertyInfo = property;
currentProperties.Add(propertyEntity);
}
return currentProperties;
}
private static List<PropertyEntity> HandleInterfaceMembers(CompareParms parms, object objectValue, Type objectType)
{
List<PropertyEntity> currentProperties = new List<PropertyEntity>();
if (parms.Config.InterfaceMembers.Count > 0)
{
Type[] interfaces = objectType.GetInterfaces();
foreach (var type in parms.Config.InterfaceMembers)
{
if (interfaces.Contains(type))
{
var properties = Cache.GetPropertyInfo(parms.Result.Config, type);
foreach (var property in properties)
{
PropertyEntity propertyEntity = new PropertyEntity();
propertyEntity.IsDynamic = false;
propertyEntity.Name = property.Name;
propertyEntity.CanRead = property.CanRead;
propertyEntity.CanWrite = property.CanWrite;
propertyEntity.PropertyType = property.PropertyType;
propertyEntity.Indexers.AddRange(property.GetIndexParameters());
propertyEntity.DeclaringType = objectType;
if (propertyEntity.Indexers.Count == 0)
{
propertyEntity.Value = property.GetValue(objectValue, null);
}
propertyEntity.PropertyInfo = property;
currentProperties.Add(propertyEntity);
}
}
}
}
if (currentProperties.Count == 0)
return null;
return currentProperties;
}
private static List<PropertyEntity> HandleDynamicObject(object objectValue, Type objectType)
{
if (TypeHelper.IsExpandoObject(objectValue))
{
return AddExpandoPropertyValues(objectValue, objectType);
}
return null;
}
private static List<PropertyEntity> AddExpandoPropertyValues(Object objectValue, Type objectType)
{
IDictionary<string, object> expandoPropertyValues = objectValue as IDictionary<string, object>;
if (expandoPropertyValues == null)
return new List<PropertyEntity>();
List<PropertyEntity> currentProperties = new List<PropertyEntity>();
foreach (var propertyValue in expandoPropertyValues)
{
PropertyEntity propertyEntity = new PropertyEntity();
propertyEntity.IsDynamic = true;
propertyEntity.Name = propertyValue.Key;
propertyEntity.Value = propertyValue.Value;
propertyEntity.CanRead = true;
propertyEntity.CanWrite = true;
propertyEntity.DeclaringType = objectType;
if (propertyValue.Value == null)
{
propertyEntity.PropertyType = null;
propertyEntity.ReflectedType = null;
}
else
{
propertyEntity.PropertyType = propertyValue.GetType();
propertyEntity.ReflectedType = propertyEntity.PropertyType;
}
currentProperties.Add(propertyEntity);
}
return currentProperties;
}
private bool IsValidIndexer(ComparisonConfig config, PropertyEntity info, string breadCrumb)
{
if (info.Indexers.Count == 0)
{
return false;
}
if (info.Indexers.Count > 1)
{
if (config.SkipInvalidIndexers)
return false;
throw new Exception("Cannot compare objects with more than one indexer for object " + breadCrumb);
}
if (info.Indexers[0].ParameterType != typeof(Int32))
{
if (config.SkipInvalidIndexers)
return false;
throw new Exception("Cannot compare objects with a non integer indexer for object " + breadCrumb);
}
#if !NETSTANDARD
var type = info.ReflectedType;
#else
var type = info.DeclaringType;
#endif
if (type == null)
{
if (config.SkipInvalidIndexers)
return false;
throw new Exception("Cannot compare objects with a null indexer for object " + breadCrumb);
}
if (type.GetProperty("Count") == null
|| type.GetProperty("Count").PropertyType != typeof(Int32))
{
if (config.SkipInvalidIndexers)
return false;
throw new Exception("Indexer must have a corresponding Count property that is an integer for object " + breadCrumb);
}
return true;
}
}
}

View File

@@ -1,141 +0,0 @@
using FSI.Lib.CompareNetObjects.IgnoreOrderTypes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two ReadOnlyCollections.
/// </summary>
public class ReadOnlyCollectionComparer : BaseTypeComparer
{
/// <summary>
/// The main constructor.
/// </summary>
public ReadOnlyCollectionComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are ReadOnlyCollections.
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsReadOnlyCollection(type1) && TypeHelper.IsReadOnlyCollection(type2);
}
/// <summary>
/// Compare two ReadOnlyCollections.
/// </summary>
public override void CompareType(CompareParms parms)
{
if (!parms.Config.CompareReadOnly)
return;
try
{
parms.Result.AddParent(parms.Object1);
parms.Result.AddParent(parms.Object2);
Type t1 = parms.Object1.GetType();
Type t2 = parms.Object2.GetType();
//Check if the class type should be excluded based on the configuration
if (ExcludeLogic.ShouldExcludeClass(parms.Config, t1, t2))
return;
parms.Object1Type = t1;
parms.Object2Type = t2;
bool countsDifferent = CollectionsDifferentCount(parms);
if (parms.Result.ExceededDifferences)
return;
if (parms.Config.IgnoreCollectionOrder)
{
IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer);
logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent);
}
else
{
CompareItems(parms);
}
}
finally
{
parms.Result.RemoveParent(parms.Object1);
parms.Result.RemoveParent(parms.Object2);
}
}
private void CompareItems(CompareParms parms)
{
int count = 0;
IEnumerator enumerator1 = ((ICollection)parms.Object1).GetEnumerator();
IEnumerator enumerator2 = ((ICollection)parms.Object2).GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count);
CompareParms childParms = new CompareParms
{
Result = parms.Result,
Config = parms.Config,
ParentObject1 = parms.Object1,
ParentObject2 = parms.Object2,
Object1 = enumerator1.Current,
Object2 = enumerator2.Current,
BreadCrumb = currentBreadCrumb
};
RootComparer.Compare(childParms);
if (parms.Result.ExceededDifferences)
return;
count++;
}
}
private bool CollectionsDifferentCount(CompareParms parms)
{
//Get count by reflection since we can't cast it to HashSet<>
int count1 = ((ICollection)parms.Object1).Count;
int count2 = ((ICollection)parms.Object2).Count;
//Objects must be the same length
if (count1 != count2)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = count1.ToString(CultureInfo.InvariantCulture),
Object2Value = count2.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Count",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
return true;
}
return false;
}
}
}

View File

@@ -1,56 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two runtime types
/// </summary>
public class RuntimeTypeComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public RuntimeTypeComparer(RootComparer rootComparer)
: base(rootComparer)
{}
/// <summary>
/// Returns true if both types are of type runtme type
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsRuntimeType(type1) && TypeHelper.IsRuntimeType(type2);
}
/// <summary>
/// Compare two runtime types
/// </summary>
public override void CompareType(CompareParms parms)
{
Type t1 = (Type)parms.Object1;
Type t2 = (Type)parms.Object2;
if (t1.FullName != t2.FullName)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = t1.FullName,
Object2Value = t2.FullName,
ChildPropertyName = "FullName",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
}
}
}
}

View File

@@ -1,49 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare primitive types (long, int, short, byte etc.) and DateTime, decimal, and Guid
/// </summary>
public class SimpleTypeComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public SimpleTypeComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if the type is a simple type
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsSimpleType(type1) && TypeHelper.IsSimpleType(type2);
}
/// <summary>
/// Compare two simple types
/// </summary>
public override void CompareType(CompareParms parms)
{
//This should never happen, null check happens one level up
if (parms.Object1 == null || parms.Object2 == null)
return;
IComparable valOne = parms.Object1 as IComparable;
if (valOne == null)
throw new Exception("Expected value does not implement IComparable");
if (valOne.CompareTo(parms.Object2) != 0)
{
AddDifference(parms);
}
}
}
}

View File

@@ -1,66 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two StringBuilders
/// </summary>
public class StringBuilderComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public StringBuilderComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are a StringBuilder
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return (TypeHelper.IsStringBuilder(type1) && TypeHelper.IsStringBuilder(type2))
|| (TypeHelper.IsStringBuilder(type1) && type2 == null)
|| (TypeHelper.IsStringBuilder(type2) && type1 == null);
}
/// <summary>
/// Compare two string builders
/// </summary>
/// <param name="parms"></param>
public override void CompareType(CompareParms parms)
{
if (parms.Config.TreatStringEmptyAndNullTheSame
&& ((parms.Object1 == null && parms.Object2 != null && parms.Object2.ToString() == string.Empty)
|| (parms.Object2 == null && parms.Object1 != null && parms.Object1.ToString() == string.Empty)))
{
return;
}
string object1String = parms.Object1.ToString();
string object2String = parms.Object2.ToString();
if (parms.Config.IgnoreStringLeadingTrailingWhitespace)
{
object1String = object1String.Trim();
object2String = object2String.Trim();
}
if (!parms.Config.CaseSensitive)
{
if (!String.Equals(object1String, object2String, StringComparison.OrdinalIgnoreCase))
{
AddDifference(parms);
}
}
else if (object1String != object2String)
{
AddDifference(parms);
}
}
}
}

View File

@@ -1,77 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two strings
/// </summary>
public class StringComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public StringComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are a string or if one is a string and one is a a null
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return (TypeHelper.IsString(type1) && TypeHelper.IsString(type2))
|| (TypeHelper.IsString(type1) && type2 == null)
|| (TypeHelper.IsString(type2) && type1 == null);
}
/// <summary>
/// Compare two strings
/// </summary>
public override void CompareType(CompareParms parms)
{
if (parms.Config.TreatStringEmptyAndNullTheSame
&& ((parms.Object1 == null && parms.Object2 != null && parms.Object2.ToString() == string.Empty)
|| (parms.Object2 == null && parms.Object1 != null && parms.Object1.ToString() == string.Empty)))
{
return;
}
if (OneOfTheStringsIsNull(parms)) return;
string string1 = parms.Object1 as string;
string string2 = parms.Object2 as string;
if (parms.Config.IgnoreStringLeadingTrailingWhitespace)
{
string1 = string1.Trim();
string2 = string2.Trim();
}
if (!parms.Config.CaseSensitive)
{
if (!String.Equals(string1, string2, StringComparison.OrdinalIgnoreCase))
{
AddDifference(parms);
}
}
else if (string1 != string2)
{
AddDifference(parms);
}
}
private bool OneOfTheStringsIsNull(CompareParms parms)
{
if (parms.Object1 == null || parms.Object2 == null)
{
AddDifference(parms);
return true;
}
return false;
}
}
}

View File

@@ -1,60 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two structs
/// </summary>
public class StructComparer : BaseTypeComparer
{
private readonly PropertyComparer _propertyComparer;
private readonly FieldComparer _fieldComparer;
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public StructComparer(RootComparer rootComparer) : base(rootComparer)
{
_propertyComparer = new PropertyComparer(rootComparer);
_fieldComparer = new FieldComparer(rootComparer);
}
/// <summary>
/// Returns true if both objects are of type struct
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsStruct(type1) && TypeHelper.IsStruct(type2);
}
/// <summary>
/// Compare two structs
/// </summary>
public override void CompareType(CompareParms parms)
{
if (parms.Result.CurrentStructDepth >= parms.Config.MaxStructDepth)
return;
try
{
parms.Result.CurrentStructDepth++;
parms.Object1Type = parms.Object1.GetType();
parms.Object2Type = parms.Object2.GetType();
if (parms.Config.CompareFields)
_fieldComparer.PerformCompareFields(parms);
if (parms.Config.CompareProperties)
_propertyComparer.PerformCompareProperties(parms);
}
finally
{
parms.Result.CurrentStructDepth--;
}
}
}
}

View File

@@ -1,54 +0,0 @@
using System;
using System.Globalization;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Logic to compare two timespans
/// </summary>
public class TimespanComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public TimespanComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both objects are timespans
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsTimespan(type1) && TypeHelper.IsTimespan(type2);
}
/// <summary>
/// Compare two timespans
/// </summary>
public override void CompareType(CompareParms parms)
{
if (((TimeSpan)parms.Object1).Ticks != ((TimeSpan)parms.Object2).Ticks)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = ((TimeSpan)parms.Object1).Ticks.ToString(CultureInfo.InvariantCulture),
Object2Value = ((TimeSpan)parms.Object2).Ticks.ToString(CultureInfo.InvariantCulture),
ChildPropertyName = "Ticks",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
}
}
}
}

View File

@@ -1,59 +0,0 @@
using System;
namespace FSI.Lib.CompareNetObjects.TypeComparers
{
/// <summary>
/// Compare two URIs
/// </summary>
public class UriComparer : BaseTypeComparer
{
/// <summary>
/// Constructor that takes a root comparer
/// </summary>
/// <param name="rootComparer"></param>
public UriComparer(RootComparer rootComparer) : base(rootComparer)
{
}
/// <summary>
/// Returns true if both types are a URI
/// </summary>
/// <param name="type1">The type of the first object</param>
/// <param name="type2">The type of the second object</param>
/// <returns></returns>
public override bool IsTypeMatch(Type type1, Type type2)
{
return TypeHelper.IsUri(type1) && TypeHelper.IsUri(type2);
}
/// <summary>
/// Compare two URIs
/// </summary>
public override void CompareType(CompareParms parms)
{
Uri uri1 = parms.Object1 as Uri;
Uri uri2 = parms.Object2 as Uri;
//This should never happen, null check happens one level up
if (uri1 == null || uri2 == null)
return;
if (uri1.OriginalString != uri2.OriginalString)
{
Difference difference = new Difference
{
ParentObject1 = parms.ParentObject1,
ParentObject2 = parms.ParentObject2,
PropertyName = parms.BreadCrumb,
Object1Value = NiceString(uri1.OriginalString),
Object2Value = NiceString(uri2.OriginalString),
ChildPropertyName = "OriginalString",
Object1 = parms.Object1,
Object2 = parms.Object2
};
AddDifference(parms.Result, difference);
}
}
}
}

View File

@@ -1,562 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
#if !NETSTANDARD1
using System.Dynamic;
#endif
#if !NETSTANDARD
using System.Data;
using System.Drawing;
using System.Runtime.CompilerServices;
#endif
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Methods for detecting types and converting types
/// </summary>
public static class TypeHelper
{
/// <summary>
/// Returns true if it is a dynamic object
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDynamicObject(Type type)
{
#if !NETSTANDARD1
return typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type);
#else
return false;
#endif
}
/// <summary>
/// Determines whether the specified object is an expando object
/// </summary>
/// <param name="objectValue">The object value.</param>
public static bool IsExpandoObject(object objectValue)
{
if (objectValue == null)
return false;
if (IsDynamicObject(objectValue.GetType()))
{
IDictionary<string, object> expandoPropertyValues = objectValue as IDictionary<string, object>;
return expandoPropertyValues != null;
}
return false;
}
/// <summary>
/// Returns true if it is a byte array
/// </summary>
public static bool IsByteArray(Type type)
{
return IsIList(type) && (
typeof(IEnumerable<byte>).IsAssignableFrom(type) ||
typeof(IEnumerable<byte?>).IsAssignableFrom(type)
)
&& type != typeof(sbyte[]);
}
/// <summary>
/// Returns true if the type can have children
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool CanHaveChildren(Type type)
{
if (type == null)
return false;
return !IsSimpleType(type)
&& !IsTimespan(type)
&& !IsDateTimeOffset(type)
&& !IsEnum(type)
&& !IsPointer(type)
&& !IsStringBuilder(type)
&& (IsClass(type)
|| IsInterface(type)
|| IsArray(type)
|| IsIDictionary(type)
|| IsIList(type)
|| IsStruct(type)
|| IsHashSet(type)
);
}
/// <summary>
/// True if the type is an array
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsArray(Type type)
{
if (type == null)
return false;
return type.IsArray;
}
/// <summary>
/// True if the type is an System.Collections.Immutable.ImmutableArray
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsImmutableArray(Type type)
{
if (type == null)
return false;
return type.Namespace == "System.Collections.Immutable"
&& type.Name == "ImmutableArray`1";
}
/// <summary>
/// True if the type is a System.Collections.ObjectModel.ReadOnlyCollection
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsReadOnlyCollection(Type type)
{
if (type == null)
return false;
return (type.Namespace == "System.Collections.ObjectModel"
&& type.Name == "ReadOnlyCollection`1");
}
/// <summary>
/// Returns true if it is a struct
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsStruct(Type type)
{
if (type == null)
return false;
return type.GetTypeInfo().IsValueType && !IsSimpleType(type) && !IsImmutableArray(type);
}
/// <summary>
/// Returns true if the type is a timespan
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsTimespan(Type type)
{
if (type == null)
return false;
return type == typeof(TimeSpan);
}
/// <summary>
/// Return true if the type is a class
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsClass(Type type)
{
if (type == null)
return false;
return type.GetTypeInfo().IsClass;
}
/// <summary>
/// Return true if the type is an interface
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsInterface(Type type)
{
if (type == null)
return false;
#if PORTABLE
return type.IsInterface;
#else
return type.GetTypeInfo().IsInterface;
#endif
}
/// <summary>
/// Return true if the type is a URI
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsUri(Type type)
{
if (type == null)
return false;
return (typeof(Uri).IsAssignableFrom(type));
}
/// <summary>
/// Return true if the type is a pointer
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsPointer(Type type)
{
if (type == null)
return false;
return type == typeof(IntPtr) || type == typeof(UIntPtr);
}
/// <summary>
/// Return true if the type is an enum
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsEnum(Type type)
{
if (type == null)
return false;
return type.GetTypeInfo().IsEnum;
}
/// <summary>
/// Return true if the type is a dictionary
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsIDictionary(Type type)
{
if (type == null)
return false;
return (typeof(IDictionary).IsAssignableFrom(type));
}
/// <summary>
/// Return true if the type is a hashset
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsHashSet(Type type)
{
if (type == null)
return false;
return type.GetTypeInfo().IsGenericType
&& type.GetTypeInfo().GetGenericTypeDefinition() == typeof(HashSet<>);
}
/// <summary>
/// Return true if the type is a List
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsIList(Type type)
{
if (type == null)
return false;
return typeof(IList).IsAssignableFrom(type) && !IsImmutableArray(type);
}
/// <summary>
/// Return true if the type is an Enumerable
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsEnumerable(Type type)
{
if (type == null)
return false;
#if !NETSTANDARD
var toCheck = type.ReflectedType;
#else
var toCheck = type.DeclaringType;
#endif
return toCheck != null && toCheck == typeof(Enumerable);
}
/// <summary>
/// Return true if the type is a Double
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDouble(Type type)
{
if (type == null)
return false;
return type == typeof(Double);
}
/// <summary>
/// Return true if the type is a Decimal
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDecimal(Type type)
{
if (type == null)
return false;
return type == typeof(Decimal);
}
/// <summary>
/// Return true if the type is a Decimal or Nullable Decimal
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsDecimal(object value)
{
if (value == null)
return false;
return value is decimal;
}
/// <summary>
/// Return true if the type is a DateTime
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDateTime(Type type)
{
if (type == null)
return false;
return type == typeof (DateTime);
}
/// <summary>
/// Return true if the type is a DateTimeOffset
/// </summary>
/// <param name="type"></param>
public static bool IsDateTimeOffset(Type type)
{
if (type == null)
return false;
return type == typeof(DateTimeOffset) || type== typeof(System.Nullable<DateTimeOffset>);
}
/// <summary>
/// Return true if the type is a StringBuilder
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsStringBuilder(Type type)
{
if (type == null)
return false;
return type == typeof(StringBuilder);
}
/// <summary>
/// Return true if the type is a string
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsString(Type type)
{
if (type == null)
return false;
return type == typeof(string);
}
/// <summary>
/// Return true if the type is a primitive type, date, decimal, string, or GUID
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsSimpleType(Type type)
{
if (type == null)
return false;
if (type.GetTypeInfo().IsGenericType && type.GetTypeInfo().GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
return type.GetTypeInfo().IsPrimitive
|| type == typeof(DateTime)
|| type == typeof(string)
|| type == typeof(Guid)
|| type == typeof(Decimal)
|| type.GetTypeInfo().IsEnum;
}
/// <summary>
/// Returns true if the Type is a Runtime type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsRuntimeType(Type type)
{
if (type == null)
return false;
return (typeof(Type).IsAssignableFrom(type));
}
/// <summary>
/// Returns true if the type is a generic type.
/// </summary>
/// <param name="type">The type.</param>
public static bool IsGenericType(Type type)
{
if (type == null)
return false;
#if NETSTANDARD1
return type.GetTypeInfo().IsGenericType;
#else
return type.IsGenericType;
#endif
}
#if NETFULL || NETSTANDARD2_0 || NETSTANDARD2_1
/// <summary>
/// Returns true if the type is an IPEndPoint
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsIpEndPoint(Type type)
{
if (type == null)
return false;
return type == typeof(IPEndPoint);
}
#endif
#if !NETSTANDARD
/// <summary>
/// Returns true if the type is a dataset
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDataset(Type type)
{
if (type == null)
return false;
return type == typeof(DataSet);
}
/// <summary>
/// Returns true if the type is a data table
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDataTable(Type type)
{
if (type == null)
return false;
return type == typeof(DataTable);
}
/// <summary>
/// Returns true if the type is a data row
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDataRow(Type type)
{
if (type == null)
return false;
return type == typeof(DataRow);
}
/// <summary>
/// Returns true if the Type is Data Column
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsDataColumn(Type type)
{
if (type == null)
return false;
return type == typeof(DataColumn);
}
/// <summary>
/// Returns true if the type is a font
/// </summary>
/// <param name="type">The type1.</param>
public static bool IsFont(Type type)
{
if (type == null)
return false;
return type == typeof(Font);
}
#endif
#if !NETSTANDARD
/// <summary>
/// Turn a list of types into a list of string types
/// </summary>
/// <param name="types"></param>
/// <returns></returns>
public static List<string> ListOfTypesSerializer(List<Type> types)
{
if (types == null || !types.Any())
return new List<string>();
List<string> result = new List<string>();
foreach (var type in types)
{
if (type != null)
{
result.Add(type.FullName);
}
}
return result;
}
/// <summary>
/// Turn a list of string into a list of types
/// </summary>
/// <param name="stringList"></param>
/// <returns></returns>
public static List<Type> ListOfTypesDeserializer(List<string> stringList)
{
if (stringList == null || !stringList.Any())
return new List<Type>();
List<Type> result = new List<Type>();
foreach (var item in stringList)
{
if (!string.IsNullOrEmpty(item))
{
result.Add(Type.GetType(item));
}
}
return result;
}
#endif
}
}

View File

@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Used internally to verify the config settings before comparing
/// </summary>
public class VerifyConfig
{
/// <summary>
/// Verifies the specified configuration.
/// </summary>
/// <param name="config">The configuration.</param>
public void Verify(ComparisonConfig config)
{
VerifySpec(config);
}
/// <summary>
/// Verifies the collection matching spec.
/// </summary>
/// <param name="config">The configuration.</param>
/// <exception cref="Exception">
/// </exception>
public void VerifySpec(ComparisonConfig config)
{
if (config.CollectionMatchingSpec == null)
return;
foreach (var kvp in config.CollectionMatchingSpec)
{
if (TypeHelper.IsIList(kvp.Key))
{
string msg = string.Format(
"Collection Matching Spec Type {0} should be a class, not a List. Expected something like Customer, not List<Customer>. See https://github.com/GregFinzer/Compare-Net-Objects/wiki/Comparing-Lists-of-Different-Lengths",
kvp.Key.Name);
throw new ArgumentException(msg, nameof(config));
}
if (!TypeHelper.IsClass(kvp.Key) && !TypeHelper.IsInterface(kvp.Key))
{
string msg = string.Format(
"Collection matching spec Type {0} should be a class or an interface. See https://github.com/GregFinzer/Compare-Net-Objects/wiki/Comparing-Lists-of-Different-Lengths",
kvp.Key.Name);
throw new ArgumentException(msg, nameof(config));
}
List<PropertyInfo> propertyInfos = Cache.GetPropertyInfo(config, kvp.Key).ToList();
foreach (var index in kvp.Value)
{
if (propertyInfos.All(o => o.Name != index))
{
string msg = string.Format("Collection Matching Spec cannot find property {0} of type {1}",
index, kvp.Key.Name);
throw new ArgumentException(msg, nameof(config));
}
}
}
}
}
}

View File

@@ -1,57 +0,0 @@
using System.Globalization;
using System.Text;
namespace FSI.Lib.CompareNetObjects
{
/// <summary>
/// Helper class for web related methods
/// </summary>
public static class WebHelper
{
/// <summary>
/// HTML-encodes a string and returns the encoded string.
/// </summary>
/// <param name="text">The text string to encode. </param>
/// <returns>The HTML-encoded text.</returns>
public static string HtmlEncode(string text)
{
if (text == null)
return null;
StringBuilder sb = new StringBuilder(text.Length);
int len = text.Length;
for (int i = 0; i < len; i++)
{
switch (text[i])
{
case '<':
sb.Append("&lt;");
break;
case '>':
sb.Append("&gt;");
break;
case '"':
sb.Append("&quot;");
break;
case '&':
sb.Append("&amp;");
break;
default:
if (text[i] > 159)
{
// decimal numeric entity
sb.Append("&#");
sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture));
sb.Append(";");
}
else
sb.Append(text[i]);
break;
}
}
return sb.ToString();
}
}
}

View File

@@ -1,54 +0,0 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="FSI.Lib.CompareNetObjects.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
</sectionGroup>
</configSections>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup><userSettings>
<FSI.Lib.CompareNetObjects.Properties.Settings>
<setting name="CompareStaticFields" serializeAs="String">
<value>True</value>
</setting>
<setting name="CompareStaticProperties" serializeAs="String">
<value>True</value>
</setting>
<setting name="ComparePrivateProperties" serializeAs="String">
<value>False</value>
</setting>
<setting name="ComparePrivateFields" serializeAs="String">
<value>False</value>
</setting>
<setting name="CompareChildren" serializeAs="String">
<value>True</value>
</setting>
<setting name="CompareReadOnly" serializeAs="String">
<value>True</value>
</setting>
<setting name="CompareFields" serializeAs="String">
<value>True</value>
</setting>
<setting name="CompareProperties" serializeAs="String">
<value>True</value>
</setting>
<setting name="Caching" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoClearCache" serializeAs="String">
<value>True</value>
</setting>
<setting name="MaxDifferences" serializeAs="String">
<value>1</value>
</setting>
<setting name="IgnoreCollectionOrder" serializeAs="String">
<value>False</value>
</setting>
<setting name="IgnoreUnknownObjectTypes" serializeAs="String">
<value>False</value>
</setting>
<setting name="IgnoreObjectDisposedException" serializeAs="String">
<value>False</value>
</setting>
</FSI.Lib.CompareNetObjects.Properties.Settings>
</userSettings>
</configuration>

View File

@@ -1,103 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ContentFile Version="2010" Moniker="..\bin\Debug\FSI.Lib.Compare-NET-Objects.dll" DocType="DX.NET" IsLocalizationDisabled="false">
<RelatedProject>documentation.dxp</RelatedProject>
<State>
<Attributes>
<Attribute key="dtscrollpos" valuetype="integer">0</Attribute>
<Attribute key="inheritedMembers" valuetype="string">on</Attribute>
<Attribute key="protectedMembers" valuetype="string">on</Attribute>
</Attributes>
</State>
<LastSelectedItem>FSI.Lib.Compare-NET-Objects</LastSelectedItem>
<item qn="FSI.Lib.Compare-NET-Objects">
<project_introduction ChangeDateTime="2012-07-20T18:35:02.4868852Z">&lt;DIV class=wikidoc&gt;&lt;A href="http://www.FSI.Lib.com"&gt;&lt;IMG style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; BORDER-TOP: medium none; BORDER-RIGHT: medium none" title="Kellerman Software Logo" alt="Kellerman Software Logo" src="http://www.FSI.Lib.com/images/logo.gif"&gt;&lt;/A&gt;&lt;BR&gt;&lt;BR&gt;&lt;B&gt;Project Description&lt;/B&gt;&lt;BR&gt;What you have been waiting for. Perform a deep compare of any two .NET objects using reflection. Shows the differences between the two objects. &lt;BR&gt;&lt;BR&gt;&lt;B&gt;NuGet Package&lt;/B&gt;&lt;BR&gt;&lt;A href="http://www.nuget.org/packages/CompareNETObjects"&gt;&lt;FONT color=#0066cc&gt;http://www.nuget.org/packages/CompareNETObjects&lt;/FONT&gt;&lt;/A&gt;&lt;BR&gt;&lt;BR&gt;Features
&lt;UL&gt;
&lt;LI&gt;Compare Primitive Types
&lt;LI&gt;Compare Structs
&lt;LI&gt;Compare IList Objects
&lt;LI&gt;Compare Single and Multi-Dimensional Arrays
&lt;LI&gt;Compare IDictionary Objects
&lt;LI&gt;Compare Publicly visible Class Fields and Properties
&lt;LI&gt;Compare Children
&lt;LI&gt;Handling for Trees with Children Pointing To Parents
&lt;LI&gt;Compare Enums
&lt;LI&gt;Compare Timespans
&lt;LI&gt;Compare Guids
&lt;LI&gt;Compare Classes that Implement IList with Indexers
&lt;LI&gt;Compare DataSet Data
&lt;LI&gt;Compare DataTable Data
&lt;LI&gt;Compare DataRow Data
&lt;LI&gt;Compare LinearGradient
&lt;LI&gt;Compare HashSet
&lt;LI&gt;Compare URI
&lt;LI&gt;Compare Types of Type (RuntimeType)
&lt;LI&gt;Source code in both C# and in VB.NET
&lt;LI&gt;NUnit Test Project Included
&lt;LI&gt;Ability to load settings from a config file for use with powershell
&lt;LI&gt;Several configuration options for comparing private elements, and ignoring specific elements.
&lt;LI&gt;Property and Field Info reflection caching for increased performance&lt;/LI&gt;&lt;/UL&gt;&lt;/DIV&gt;</project_introduction>
<project_gettingstarted ChangeDateTime="2012-12-21T18:22:31.6648108Z">&lt;P class=wikidoc&gt;&lt;STRONG&gt;Important&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class=wikidoc&gt;
&lt;UL&gt;
&lt;LI&gt;By default for performance reasons, Compare .NET Objects only detects the first difference.&amp;nbsp; To capture all differences set MaxDifferences to the maximum differences desired.
&lt;LI&gt;After the comparison, the differences are in the Differences list or in the DifferencesString properties.
&lt;LI&gt;By default, a deep comparison is performed.&amp;nbsp; To perform a shallow comparison, set CompareChildren = false
&lt;LI&gt;By default, private properties and fields are not compared.&amp;nbsp; Set ComparePrivateProperties and ComparePrivateFields to true to override this behavior. &lt;/LI&gt;&lt;/UL&gt;&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;C# Example&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR: white; COLOR: black"&gt;&lt;PRE&gt;&lt;SPAN style="COLOR: green"&gt;//This is the comparison class&lt;/SPAN&gt;
CompareObjects compareObjects = &lt;SPAN style="COLOR: blue"&gt;new&lt;/SPAN&gt; CompareObjects();
&lt;SPAN style="COLOR: green"&gt;//Create a couple objects to compare&lt;/SPAN&gt;
Person person1 = &lt;SPAN style="COLOR: blue"&gt;new&lt;/SPAN&gt; Person();
person1.DateCreated = DateTime.Now;
person1.Name = &lt;SPAN style="COLOR: #a31515"&gt;"Greg"&lt;/SPAN&gt;;
Person person2 = &lt;SPAN style="COLOR: blue"&gt;new&lt;/SPAN&gt; Person();
person2.Name = &lt;SPAN style="COLOR: #a31515"&gt;"John"&lt;/SPAN&gt;;
person2.DateCreated = person1.DateCreated;
&lt;SPAN style="COLOR: green"&gt;//These will be different, write out the differences&lt;/SPAN&gt;
&lt;SPAN style="COLOR: blue"&gt;if&lt;/SPAN&gt; (!compareObjects.Compare(person1, person2))
Console.WriteLine(compareObjects.DifferencesString);
&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;VB.NET Example&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR: white; COLOR: black"&gt;&lt;PRE&gt;&lt;SPAN style="COLOR: green"&gt;'This is the comparison class&lt;/SPAN&gt;
&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; compareObjects &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; CompareObjects()
&lt;SPAN style="COLOR: green"&gt;'Create a couple objects to compare&lt;/SPAN&gt;
&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; person1 &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; Person()&lt;BR&gt;person1.DateCreated = &lt;SPAN style="COLOR: blue"&gt;Date&lt;/SPAN&gt;.Now
person1.Name = &lt;SPAN style="COLOR: #a31515"&gt;"Greg"&lt;/SPAN&gt;
&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; person2 &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; Person()
person2.Name = &lt;SPAN style="COLOR: #a31515"&gt;"John"&lt;/SPAN&gt;
person2.DateCreated = person1.DateCreated
&lt;SPAN style="COLOR: green"&gt;'These will be different, write out the differences&lt;/SPAN&gt;
&lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Not&lt;/SPAN&gt; compareObjects.Compare(person1, person2) &lt;SPAN style="COLOR: blue"&gt;Then&lt;/SPAN&gt;
Console.WriteLine(compareObjects.DifferencesString)
&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt;
&lt;/PRE&gt;&lt;/DIV&gt;</project_gettingstarted>
</item>
<item qn="FSI.Lib.Compare-NET-Objects~FSI.Lib.CompareNetObjects_namespace">
<summary ChangeDateTime="2012-07-20T18:37:25.5550682Z">Compare .NET Objects</summary>
</item>
<Filters>
<CONTENTSTORECOMMON>
<IsIncludedItemsOnly>true</IsIncludedItemsOnly>
</CONTENTSTORECOMMON>
<CONTENTSTORELOCALIZATION>
<IsPrimaryLocaleContentPreviewEnabled>true</IsPrimaryLocaleContentPreviewEnabled>
</CONTENTSTORELOCALIZATION>
<DX.NETCONTENTSTOREFILTER>
<ShowProtectedMembers>true</ShowProtectedMembers>
<ShowProtectedFriendMembers>true</ShowProtectedFriendMembers>
<ShowFriendMembers>false</ShowFriendMembers>
<ShowPrivateMembers>false</ShowPrivateMembers>
<ShowInheritedMembers>false</ShowInheritedMembers>
<IsSourceCodeContentVisible>true</IsSourceCodeContentVisible>
</DX.NETCONTENTSTOREFILTER>
</Filters>
</ContentFile>

View File

@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationWorkspace>
<RecentItems>
<RecentItem Key="0d9294fc-870e-4eb0-8d4e-a2fc0feef44f.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="2169f8f6-cac4-40ca-93b2-f02fefe798a9.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="eb1abafe-c230-45d7-b8dc-7bf3f5c20140.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="ebeae5b6-88ec-43e3-a898-92db1bcc4ba0.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="c669abc0-d388-48b8-ac62-15d9e7b5c797.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="b3f5ba5b-0359-4a92-b95c-8d4e952b3c5f.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="5380cb06-997d-4df1-8129-cfa28761d20f.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="cb91e07d-611c-4efa-84e5-a92187c24b81.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="7ceb9cfa-d0df-4c9c-b9df-ab271bccb7f7.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="8aaa1a6f-70a8-4a3d-8b69-dc429b86377a.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="10fb8965-c607-4150-be93-4219657192f9.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="7a032303-9805-4638-b34c-69a6db868ffc.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="0d061814-6a12-4d44-bb74-a2809f19fce6.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="6e4dfa3a-41cc-4f4f-933d-acc4ea832f0b.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="c5780969-16a4-48b1-8dac-34c0292912df.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="a6674676-6015-4a01-a478-a0ea8fe0510a.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="eee6a46b-ecf1-47b6-8a06-6f7e574c06ae.-" IsPinned="false" Type="Innovasys.ContentAuthoring.Topics.RecentTopic" Assembly="Innovasys.ContentAuthoring.Topics">
<HandlerName>TOPICS</HandlerName>
<Category>Topics</Category>
</RecentItem>
<RecentItem Key="FSI.Lib.Compare-NET-Objects.dxc.-" IsPinned="false" Type="Innovasys.DocumentX.ContentFiles.RecentContentFile" Assembly="Innovasys.DocumentX.ContentFiles">
<HandlerName>CONTENT FILES</HandlerName>
<Category>Content Files</Category>
</RecentItem>
</RecentItems>
<ProjectProfileApplicationPlugin DesignTimeProjectProfile="39caa9b0-295d-4fcc-9037-4bc5f2195e31" DesignTimeProjectLocale="-" />
</ApplicationWorkspace>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,78 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace FSI.Lib.DeEncryptString
{
public static class DeEncrypt
{
/// <summary>
/// Encrypts the string.
/// </summary>
/// <param name="clearText">The clear text.</param>
/// <param name="Key">The key.</param>
/// <param name="IV">The IV.</param>
/// <returns></returns>
private static byte[] CryptString(byte[] clearText, byte[] Key, byte[] IV)
{
MemoryStream ms = new MemoryStream();
Rijndael alg = Rijndael.Create();
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearText, 0, clearText.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
return encryptedData;
}
/// <summary>
/// Encrypts the string.
/// </summary>
/// <param name="clearText">The clear text.</param>
/// <param name="Password">The password.</param>
/// <returns></returns>
public static string CryptString(string clearText, string Password)
{
byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
byte[] encryptedData = CryptString(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
return Convert.ToBase64String(encryptedData);
}
/// <summary>
/// Decrypts the string.
/// </summary>
/// <param name="cipherData">The cipher data.</param>
/// <param name="Key">The key.</param>
/// <param name="IV">The IV.</param>
/// <returns></returns>
private static byte[] DecryptString(byte[] cipherData, byte[] Key, byte[] IV)
{
MemoryStream ms = new MemoryStream();
var alg = Rijndael.Create();
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
/// <summary>
/// Decrypts the string.
/// </summary>
/// <param name="cipherText">The cipher text.</param>
/// <param name="Password">The password.</param>
/// <returns></returns>
public static string DecryptString(string cipherText, string Password)
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
byte[] decryptedData = DecryptString(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));
return System.Text.Encoding.Unicode.GetString(decryptedData);
}
}
}

View File

@@ -1,704 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
namespace FSI.Lib.EasyEncryption
{
/// <summary>
/// Encryption algorithm types.
/// </summary>
public enum EncryptionAlgorithm
{
/// <summary>
/// Specifies the Advanced Encryption Standard (AES) symmetric encryption algorithm.
/// </summary>
Aes,
/// <summary>
/// Specifies the Data Encryption Standard (DES) symmetric encryption algorithm.
/// </summary>
Des,
/// <summary>
/// Specifies the RC2 symmetric encryption algorithm.
/// </summary>
Rc2,
/// <summary>
/// Specifies the Rijndael symmetric encryption algorithm.
/// </summary>
Rijndael,
/// <summary>
/// Specifies the TripleDES symmetric encryption algorithm.
/// </summary>
TripleDes
}
/// <summary>
/// Class to provide encryption and decryption services.
/// </summary>
/// <remarks>
/// <para>
/// The Encryption class performs encryption and decryption using the specified algorithm.
/// </para>
/// <para>
/// The encrypted values may appear considerably larger than the decrypted values
/// because encrypted values contains some additional meta data. If either data size or
/// performance is a concern, use the <see cref="CreateStreamReader(Stream)"/> or
/// <see cref="CreateStreamWriter(Stream)"/> methods to work with the streaming classes
/// instead.
/// </para>
/// </remarks>
public class Encryption
{
private static readonly int SaltLength = 8;
private static readonly Dictionary<EncryptionAlgorithm, Type> AlgorithmLookup = new Dictionary<EncryptionAlgorithm, Type>
{
[EncryptionAlgorithm.Aes] = typeof(Aes),
[EncryptionAlgorithm.Des] = typeof(DES),
[EncryptionAlgorithm.Rc2] = typeof(RC2),
[EncryptionAlgorithm.Rijndael] = typeof(Rijndael),
[EncryptionAlgorithm.TripleDes] = typeof(TripleDES),
};
private string Password { get; }
private Type AlgorithmType { get; }
/// <summary>
/// Converts a byte array to a string.
/// </summary>
/// <param name="bytes">The byte array to be converted.</param>
/// <returns>Returns the converted string.</returns>
public static string EncodeBytesToString(byte[] bytes) => Convert.ToBase64String(bytes);
/// <summary>
/// Converts a string to a byte array.
/// </summary>
/// <param name="s">The string to be converted.</param>
/// <returns>Returns the converted byte array.</returns>
public static byte[] DecodeBytesFromString(string s) => Convert.FromBase64String(s);
/// <summary>
/// Constructs a new <c>Encryption</c> instance.
/// </summary>
/// <param name="password">Specifies the encryption password. Leading and trailing spaces are removed.</param>
/// <param name="algorithm">Specifies which encryption algorithm is used.</param>
public Encryption(string password, EncryptionAlgorithm algorithm)
{
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is required.", nameof(password));
Password = password.Trim();
AlgorithmType = AlgorithmLookup[algorithm];
}
#region Encryption stream creation methods
/// <summary>
/// Creates an <see cref="EncryptionWriter"/> instance using the specified stream.
/// </summary>
/// <remarks>
/// The <see cref="EncryptionWriter"/> class is the preferred method to encrypt data
/// as it will store some necessary meta data only once for all data in the stream.
/// It is also more performant. The other encryption methods defer to the EncryptionWriter
/// class for actual encryption.
/// </remarks>
/// <param name="stream">The stream the encrypted data will be written to.</param>
/// <returns>An instance of the <see cref="EncryptionWriter"/> class.</returns>
public EncryptionWriter CreateStreamWriter(Stream stream)
{
// Create a random salt and write to stream
byte[] salt = CreateSalt();
stream.Write(salt, 0, salt.Length);
// Create symmetric algorithm
SymmetricAlgorithm algorithm = CreateAlgorithm();
algorithm.Padding = PaddingMode.PKCS7;
// Create key and IV
byte[] key, iv;
GenerateKeyAndIv(algorithm, salt, out key, out iv);
// Create EncryptionWriter
ICryptoTransform encryptor = algorithm.CreateEncryptor(key, iv);
CryptoStream cs = new CryptoStream(stream, encryptor, CryptoStreamMode.Write);
return new EncryptionWriter(algorithm, encryptor, stream);
}
/// <summary>
/// Creates an <see cref="EncryptionWriter"/> instance using the specified file name.
/// </summary>
/// <remarks>
/// The <see cref="EncryptionWriter"/> class is the preferred method to encrypt data
/// as it will store some necessary meta data only once for all data in the stream.
/// It is also more performant. The other encryption methods defer to the EncryptionWriter
/// class for actual encryption.
/// </remarks>
/// <param name="path">The file name the encrypted data will be written to.</param>
/// <returns>An instance of the <see cref="EncryptionWriter"/> class.</returns>
public EncryptionWriter CreateStreamWriter(string path)
{
return CreateStreamWriter(File.Open(path, FileMode.OpenOrCreate, FileAccess.Write));
}
/// <summary>
/// Creates an <see cref="EncryptionReader"/> instance using the specified stream.
/// </summary>
/// <remarks>
/// The <see cref="EncryptionReader"/> class is the preferred method to decrypt data.
/// It is also more performant. The other decryption methods defer to the EncryptionReader
/// class for actual decryption.
/// </remarks>
/// <param name="stream">The stream the encrypted data will be read from.</param>
/// <returns>An instance of the <see cref="EncryptionReader"/> class.</returns>
public EncryptionReader CreateStreamReader(Stream stream)
{
// Read salt from input stream
byte[] salt = new byte[SaltLength];
if (stream.Read(salt, 0, salt.Length) < SaltLength)
throw new ArgumentOutOfRangeException("Reached end of input stream before reading encryption metadata.");
// Create symmetric algorithm
SymmetricAlgorithm algorithm = CreateAlgorithm();
algorithm.Padding = PaddingMode.PKCS7;
// Create key and IV
byte[] key, iv;
GenerateKeyAndIv(algorithm, salt, out key, out iv);
// Create EncryptionReader
ICryptoTransform decryptor = algorithm.CreateDecryptor(key, iv);
CryptoStream cs = new CryptoStream(stream, decryptor, CryptoStreamMode.Read);
return new EncryptionReader(algorithm, decryptor, stream);
}
/// <summary>
/// Creates an <see cref="EncryptionReader"/> instance using the specified file name.
/// </summary>
/// <remarks>
/// The <see cref="EncryptionReader"/> class is the preferred method to decrypt data.
/// It is also more performant. The other decryption methods defer to the EncryptionReader
/// class for actual decryption.
/// </remarks>
/// <param name="path">The file name the encrypted data will be read from.</param>
/// <returns>An instance of the <see cref="EncryptionReader"/> class.</returns>
public EncryptionReader CreateStreamReader(string path)
{
return CreateStreamReader(File.Open(path, FileMode.Open, FileAccess.Read));
}
#endregion
#region Encryption
/// <summary>
/// Encrypts a <c>string</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(string value) => Encrypt(w => w.Write(value ?? string.Empty));
/// <summary>
/// Encrypts a <c>bool</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(bool value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>char</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(char value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>sbyte</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(sbyte value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>byte</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(byte value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>short</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(short value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>ushort</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(ushort value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>int</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(int value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>uint</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(uint value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>long</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(long value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>ulong</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(ulong value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>float</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(float value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>double</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(double value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>decimal</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(decimal value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>DateTime</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(DateTime value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>byte[]</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(byte[] value) => Encrypt(w => w.Write(value));
/// <summary>
/// Encrypts a <c>string[]</c> value.
/// </summary>
/// <remarks>
/// The encrypted value will be larger than the unencrypted value because the
/// encrypted value will contain some additional meta data. To minimize the
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
/// to create a <see cref="EncryptionWriter"/> instead.
/// </remarks>
/// <param name="value">The value to decrypt.</param>
/// <returns>Returns the encrypted value</returns>
public string Encrypt(string[] value) => Encrypt(w => w.Write(value));
private string Encrypt(Action<EncryptionWriter> action)
{
using (MemoryStream stream = new MemoryStream())
using (EncryptionWriter writer = CreateStreamWriter(stream))
{
action(writer);
return EncodeBytesToString(stream.ToArray());
}
}
#endregion
#region Decryption
/// <summary>
/// Decrypts a <c>string</c> value encrypted with <see cref="Encrypt(string)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public string DecryptString(string encryptedValue) => (string)Decrypt(encryptedValue, r => r.ReadString());
/// <summary>
/// Decrypts a <c>bool</c> value encrypted with <see cref="Encrypt(bool)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public bool DecryptBoolean(string encryptedValue) => (bool)Decrypt(encryptedValue, r => r.ReadBoolean());
/// <summary>
/// Decrypts a <c>char</c> value encrypted with <see cref="Encrypt(char)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public char DecryptChar(string encryptedValue) => (char)Decrypt(encryptedValue, r => r.ReadChar());
/// <summary>
/// Decrypts a <c>sbyte</c> value encrypted with <see cref="Encrypt(sbyte)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public sbyte DecryptSByte(string encryptedValue) => (sbyte)Decrypt(encryptedValue, r => r.ReadSByte());
/// <summary>
/// Decrypts a <c>byte</c> value encrypted with <see cref="Encrypt(byte)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public byte DecryptByte(string encryptedValue) => (byte)Decrypt(encryptedValue, r => r.ReadByte());
/// <summary>
/// Decrypts a <c>short</c> value encrypted with <see cref="Encrypt(short)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public short DecryptInt16(string encryptedValue) => (short)Decrypt(encryptedValue, r => r.ReadInt16());
/// <summary>
/// Decrypts a <c>ushort</c> value encrypted with <see cref="Encrypt(ushort)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public ushort DecryptUInt16(string encryptedValue) => (ushort)Decrypt(encryptedValue, r => r.ReadUInt16());
/// <summary>
/// Decrypts a <c>int</c> value encrypted with <see cref="Encrypt(int)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public int DecryptInt32(string encryptedValue) => (int)Decrypt(encryptedValue, r => r.ReadInt32());
/// <summary>
/// Decrypts a <c>uint</c> value encrypted with <see cref="Encrypt(uint)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public uint DecryptUInt32(string encryptedValue) => (uint)Decrypt(encryptedValue, r => r.ReadUInt32());
/// <summary>
/// Decrypts a <c>long</c> value encrypted with <see cref="Encrypt(long)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public long DecryptInt64(string encryptedValue) => (long)Decrypt(encryptedValue, r => r.ReadInt64());
/// <summary>
/// Decrypts a <c>ulong</c> value encrypted with <see cref="Encrypt(ulong)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public ulong DecryptUInt64(string encryptedValue) => (ulong)Decrypt(encryptedValue, r => r.ReadUInt64());
/// <summary>
/// Decrypts a <c>float</c> value encrypted with <see cref="Encrypt(float)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public float DecryptSingle(string encryptedValue) => (float)Decrypt(encryptedValue, r => r.ReadSingle());
/// <summary>
/// Decrypts a <c>double</c> value encrypted with <see cref="Encrypt(double)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public double DecryptDouble(string encryptedValue) => (double)Decrypt(encryptedValue, r => r.ReadDouble());
/// <summary>
/// Decrypts a <c>decimal</c> value encrypted with <see cref="Encrypt(decimal)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public decimal DecryptDecimal(string encryptedValue) => (decimal)Decrypt(encryptedValue, r => r.ReadDecimal());
/// <summary>
/// Decrypts a <c>DateTime</c> value encrypted with <see cref="Encrypt(DateTime)"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public DateTime DecryptDateTime(string encryptedValue) => (DateTime)Decrypt(encryptedValue, r => r.ReadDateTime());
/// <summary>
/// Decrypts a <c>byte[]</c> value encrypted with <see cref="Encrypt(byte[])"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public byte[] DecryptByteArray(string encryptedValue) => (byte[])Decrypt(encryptedValue, r => r.ReadByteArray());
/// <summary>
/// Decrypts a <c>string[]</c> value encrypted with <see cref="Encrypt(string[])"/>.
/// </summary>
/// <param name="encryptedValue">The value to decrypt.</param>
/// <returns>Returns the decrypted value</returns>
public string[] DecryptStringArray(string encryptedValue) => (string[])Decrypt(encryptedValue, r => r.ReadStringArray());
private object Decrypt(string encryptedValue, Func<EncryptionReader, object> action)
{
using (MemoryStream stream = new MemoryStream(DecodeBytesFromString(encryptedValue)))
using (EncryptionReader reader = CreateStreamReader(stream))
{
return action(reader);
}
}
#endregion
#region Encryption/decryption of objects
/// <summary>
/// Class to hold encrypt/decrypt functions for each supported data type.
/// </summary>
private class TypeInfo
{
public Func<Encryption, object, string> Encrypt { get; set; }
public Func<Encryption, string, object> Decrypt { get; set; }
public TypeInfo(Func<Encryption, object, string> encrypt, Func<Encryption, string, object> decrypt)
{
Encrypt = encrypt;
Decrypt = decrypt;
}
}
private static readonly Dictionary<Type, TypeInfo> TypeInfoLookup = new Dictionary<Type, TypeInfo>()
{
[typeof(string)] = new TypeInfo((e, v) => e.Encrypt((string)v), (e, s) => e.DecryptString(s)),
[typeof(bool)] = new TypeInfo((e, v) => e.Encrypt((bool)v), (e, s) => e.DecryptBoolean(s)),
[typeof(char)] = new TypeInfo((e, v) => e.Encrypt((char)v), (e, s) => e.DecryptChar(s)),
[typeof(sbyte)] = new TypeInfo((e, v) => e.Encrypt((sbyte)v), (e, s) => e.DecryptSByte(s)),
[typeof(byte)] = new TypeInfo((e, v) => e.Encrypt((byte)v), (e, s) => e.DecryptByte(s)),
[typeof(short)] = new TypeInfo((e, v) => e.Encrypt((short)v), (e, s) => e.DecryptInt16(s)),
[typeof(ushort)] = new TypeInfo((e, v) => e.Encrypt((ushort)v), (e, s) => e.DecryptUInt16(s)),
[typeof(int)] = new TypeInfo((e, v) => e.Encrypt((int)v), (e, s) => e.DecryptInt32(s)),
[typeof(uint)] = new TypeInfo((e, v) => e.Encrypt((uint)v), (e, s) => e.DecryptUInt32(s)),
[typeof(long)] = new TypeInfo((e, v) => e.Encrypt((long)v), (e, s) => e.DecryptInt64(s)),
[typeof(ulong)] = new TypeInfo((e, v) => e.Encrypt((ulong)v), (e, s) => e.DecryptUInt64(s)),
[typeof(float)] = new TypeInfo((e, v) => e.Encrypt((float)v), (e, s) => e.DecryptSingle(s)),
[typeof(double)] = new TypeInfo((e, v) => e.Encrypt((double)v), (e, s) => e.DecryptDouble(s)),
[typeof(decimal)] = new TypeInfo((e, v) => e.Encrypt((decimal)v), (e, s) => e.DecryptDecimal(s)),
[typeof(DateTime)] = new TypeInfo((e, v) => e.Encrypt((DateTime)v), (e, s) => e.DecryptDateTime(s)),
[typeof(byte[])] = new TypeInfo((e, v) => e.Encrypt((byte[])v), (e, s) => e.DecryptByteArray(s)),
[typeof(string[])] = new TypeInfo((e, v) => e.Encrypt((string[])v), (e, s) => e.DecryptStringArray(s)),
};
/// <summary>
/// Indicates if the specified data type is supported by the encryption and decryption methods.
/// </summary>
/// <remarks>
/// The encryption code supports all basic .NET data types in addition to <c>byte[]</c>
/// and <c>string[]</c>. More complex data types are not supported.
/// </remarks>
/// <param name="type">The data type to be tested.</param>
/// <returns>True if the specified type is supported. False otherwise.</returns>
public static bool IsTypeSupported(Type type) => TypeInfoLookup.ContainsKey(type);
/// <summary>
/// Encrypts an object value. The object must hold one of the supported data types.
/// </summary>
/// <param name="value">Object to be encrypted.</param>
/// <exception cref="ArgumentException"><paramref name="value"/> holds an unsupported data type.</exception>
/// <returns>An encrypted string that can be decrypted using Decrypt.</returns>
public string Encrypt(object value)
{
if (value == null)
return null;
if (TypeInfoLookup.TryGetValue(value.GetType(), out TypeInfo info))
return info.Encrypt(this, value);
throw new ArgumentException(string.Format("Cannot encrypt value : Data type '{0}' is not supported", value.GetType()));
}
/// <summary>
/// Decrypts an object value of the specified type.
/// </summary>
/// <param name="encryptedValue">The encrypted string to be decrypted.</param>
/// <param name="targetType">The type of data that was originally encrypted into <paramref name="encryptedValue"/>.</param>
/// <exception cref="ArgumentException"></exception>
/// <returns>Returns the decrypted value.</returns>
public object Decrypt(string encryptedValue, Type targetType)
{
if (TypeInfoLookup.TryGetValue(targetType, out TypeInfo info))
return info.Decrypt(this, encryptedValue);
throw new ArgumentException(string.Format("Cannot decrypt value : Data type '{0}' is not supported", targetType));
}
#endregion
#region Support methods
/// <summary>
/// Creates a SymmetricAlgorithm instance for the current encryption algorithm.
/// </summary>
/// <returns>
/// Returns the created SymmetricAlgorithm instance.
/// </returns>
protected SymmetricAlgorithm CreateAlgorithm()
{
MethodInfo method = AlgorithmType.GetMethod("Create", Array.Empty<Type>());
if (method != null)
{
if (method.Invoke(null, null) is SymmetricAlgorithm algorithm)
return algorithm;
}
throw new Exception($"Unable to create instance of {AlgorithmType.FullName}.");
}
/// <summary>
/// Generates a salt that contains a cryptographically strong sequence of random values.
/// </summary>
/// <returns>The generated salt value.</returns>
private byte[] CreateSalt()
{
byte[] salt = new byte[SaltLength];
using (RNGCryptoServiceProvider generator = new RNGCryptoServiceProvider())
{
generator.GetBytes(salt);
}
return salt;
}
/// <summary>
/// Generates a pseudorandom key and initialization vector from the current password and the
/// given salt.
/// </summary>
/// <param name="algorithm"><see cref="SymmetricAlgorithm"></see> being used to encrypt.</param>
/// <param name="salt">The salt used to derive the key and initialization vector.</param>
/// <param name="key">Returns the generated key.</param>
/// <param name="iv">Returns the generated initialization vector.</param>
protected void GenerateKeyAndIv(SymmetricAlgorithm algorithm, byte[] salt, out byte[] key, out byte[] iv)
{
int keyLength = algorithm.KeySize >> 3;
int ivLength = algorithm.BlockSize >> 3;
byte[] bytes = DeriveBytes(salt, keyLength + ivLength);
key = new byte[keyLength];
Buffer.BlockCopy(bytes, 0, key, 0, keyLength);
iv = new byte[ivLength];
Buffer.BlockCopy(bytes, keyLength, iv, 0, ivLength);
}
/// <summary>
/// Generates a series of pseudorandom bytes of the specified length based on the current
/// password and the given salt.
/// </summary>
/// <param name="salt">The salt used to derive the bytes.</param>
/// <param name="bytes">The number of bits of data to generate.</param>
/// <returns>Returns the derived bytes.</returns>
protected byte[] DeriveBytes(byte[] salt, int bytes)
{
Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(Password, salt, 1000);
return derivedBytes.GetBytes(bytes);
}
#endregion
}
}

View File

@@ -1,105 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace FSI.Lib.EasyEncryption
{
/// <summary>
/// Class that provides streaming decryption functionality.
/// </summary>
/// <remarks>
/// Using this class is the preferred way to decrypt values from a file or memory.
/// Other decryption methods defer to this class for actual decryption.
/// </remarks>
public class EncryptionReader : BinaryReader, IDisposable
{
private SymmetricAlgorithm Algorithm;
private ICryptoTransform Decryptor;
internal EncryptionReader(SymmetricAlgorithm algorithm, ICryptoTransform decryptor, Stream stream) : base(stream)
{
Algorithm = algorithm;
Decryptor = decryptor;
}
/// <summary>
/// Reads a <c>DateTime</c> value from the encrypted stream.
/// </summary>
/// <returns>The decrypted value.</returns>
public DateTime ReadDateTime()
{
return new DateTime(ReadInt64());
}
/// <summary>
/// Reads a <c>byte[]</c> value from the encrypted stream.
/// </summary>
/// <returns>The decrypted values.</returns>
public byte[] ReadByteArray()
{
int count = ReadInt32();
byte[] bytes = new byte[count];
if (count > 0)
Read(bytes, 0, count);
return bytes;
}
/// <summary>
/// Reads a <c>string[]</c> value from the encrypted stream.
/// </summary>
/// <returns>The decrypted values.</returns>
public string[] ReadStringArray()
{
int count = ReadInt32();
string[] strings = new string[count];
for (int i = 0; i < count; i++)
strings[i] = ReadString();
return strings;
}
#region IDisposable implementation
private bool disposed = false; // To detect redundant calls
/// <summary>
/// Releases all resources used by the current instance of the <c>EncryptionReader</c> class.
/// </summary>
public new void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the <c>EncryptionReader</c> class and optionally
/// releases the managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
if (!disposed)
{
disposed = true;
if (disposing)
{
// Dispose managed objects
base.Dispose(true);
Decryptor.Dispose();
Algorithm.Dispose();
}
}
}
/// <summary>
/// Destructs this instance of <c>EncryptionReader</c>.
/// </summary>
~EncryptionReader()
{
Dispose(false);
}
#endregion
}
}

View File

@@ -1,105 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace FSI.Lib.EasyEncryption
{
/// <summary>
/// Class that provides streaming encryption functionality.
/// </summary>
/// <remarks>
/// Using this class is the preferred way to encrypt values to a file or memory.
/// Other encryption methods defer to this class for actual encryption. Meta data
/// that must be stored with the encrypted result is only stored once for all
/// data in the stream.
/// </remarks>
public class EncryptionWriter : BinaryWriter, IDisposable
{
private readonly SymmetricAlgorithm Algorithm;
private readonly ICryptoTransform Encryptor;
internal EncryptionWriter(SymmetricAlgorithm algorithm, ICryptoTransform encryptor, Stream stream) : base(stream)
{
Algorithm = algorithm;
Encryptor = encryptor;
}
/// <summary>
/// Writes a <c>DateTime</c> value to the encrypted stream.
/// </summary>
/// <param name="value"><c>DateTime</c> value to write.</param>
public void Write(DateTime value)
{
Write(value.Ticks);
}
/// <summary>
/// Writes a <c>byte</c> array to the encrypted stream.
/// </summary>
/// <remarks>
/// Note: Hides <c>BinaryWriter.Write(byte[])</c>.
/// </remarks>
/// <param name="value"><c>byte[]</c> values to write.</param>
public new void Write(byte[] value)
{
Write(value.Length);
Write(value, 0, value.Length);
}
/// <summary>
/// Writes a <c>string</c> to the encrypted stream.
/// </summary>
/// <param name="value"><c>string[]</c> values to write.</param>
public void Write(string[] value)
{
Write(value.Length);
for (int i = 0; i < value.Length; i++)
Write(value[i]);
}
#region IDisposable implementation
private bool disposed = false; // To detect redundant calls
/// <summary>
/// Releases all resources used by the current instance of the <c>EncryptionWriter</c> class.
/// </summary>
public new void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the <c>EncryptionWriter</c> class and optionally
/// releases the managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
if (!disposed)
{
disposed = true;
if (disposing)
{
// Dispose managed objects
base.Dispose(true);
Encryptor.Dispose();
Algorithm.Dispose();
}
}
}
/// <summary>
/// Destructs this instance of <c>EncryptionWriter</c>.
/// </summary>
~EncryptionWriter()
{
Dispose(false);
}
#endregion
}
}

View File

@@ -1,90 +0,0 @@
# EasyEncryption
[![NuGet version (SoftCircuits.EasyEncryption)](https://img.shields.io/nuget/v/SoftCircuits.EasyEncryption.svg?style=flat-square)](https://www.nuget.org/packages/SoftCircuits.EasyEncryption/)
```
Install-Package SoftCircuits.EasyEncryption
```
The .NET Framework provides a number of encryption routines. However, these routines generally require a bit of work to set up correctly. Use `EasyEncryption` to make these encryption routines more easily accessible.
## Encrypting a String
The `Encrypt()` method can be used to encrypt a string. Use `DecryptString()` to decrypt the string back to the original.
```cs
Encryption encrypt = new Encryption("Password123", EncryptionAlgorithm.TripleDes);
string original = "This is my message";
string cipher = encrypt.Encrypt(original);
string result = encrypt.DecryptString(cipher);
Debug.Assert(result == message);
```
## Encrypting other Types
The `Encrypt()` method is overloaded to encrypt many different data types. When decrypting, you must use the decryption method specific to the data type you are decrypting. This example encrypts an `int` and `double` value.
```cs
Encryption encrypt = new Encryption("Password123", EncryptionAlgorithm.TripleDes);
int originalInt = 55;
double originalDouble = 123.45;
string cipherInt = encrypt.Encrypt(originalInt);
string cipherDouble = encrypt.Encrypt(originalDouble);
int resultInt = encrypt.DecryptInt32(cipherInt);
double resultDouble = encrypt.DecryptDouble(cipherDouble);
Debug.Assert(resultInt == originalInt);
Debug.Assert(resultDouble == originalDouble);
```
## Streams
`EasyEncryption` also provides the streaming classes `EncryptionWriter` and `EncryptionReader`. These classes work well when encrypting to (or decrypting from) files.
The following example uses the `CreateStreamWriter()` to encrypt a number of integer values to a file.
```cs
Encryption encrypt = new Encryption("Password123", EncryptionAlgorithm.TripleDes);
int[] intValues = { 123, 88, 902, 27, 16, 4, 478, 54 };
using (EncryptionWriter writer = encrypt.CreateStreamWriter(path))
{
for (int i = 0; i < intValues.Length; i++)
writer.Write(intValues[i]);
}
```
Use the `CreateStreamReader()` method to decrypt those integer values from the file.
```cs
Encryption encrypt = new Encryption("Password123", EncryptionAlgorithm.TripleDes);
int[] intValues = new int[8];
using (EncryptionReader reader = encrypt.CreateStreamReader(path))
{
for (int i = 0; i < intValues.Length; i++)
intValues[i] = reader.ReadInt32();
}
```
Also, the `CreateStreamWriter()` and `CreateStreamReader()` methods are overloaded to accept a stream argument, allowing you to use custom streams. For example, you could use a `MemoryStream` to encrypt data to memory. This is demonstrated in the following example. It also uses the static method `EncodeBytesToString()` method to convert the results to a string. (Note that there is also a corresponding static `DecodeBytesFromString()` method.)
```cs
Encryption encrypt = new Encryption("Password123", EncryptionAlgorithm.TripleDes);
using (MemoryStream stream = new MemoryStream())
using (EncryptionWriter writer = encrypt.CreateStreamWriter(stream))
{
writer.Write("ABC");
writer.Write(123);
writer.Write(123.45);
string s = Encryption.EncodeBytesToString(stream.ToArray());
}
```
Note that the streaming classes are actually the most efficient way to encrypt and decrypt data. In fact the `Encrypt()` and decryption methods create an instance of `EncryptionWriter` internally (using a `MemoryStream`), even when only encrypting or decrypting a single byte.
In addition, it should be pointed out that the encrypted results produced by these routines include embedded meta data, making the encrypted data slightly larger than it would otherwise be. However, when encrypting to a stream, this data would only be stored once regardless of the number of values added to the stream. The takeaway is that you can use the `Encrypt()` method for a simple encryption, but should use the streaming classes for anything more complex.

View File

@@ -1,48 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<OutputType>Library</OutputType>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<AssemblyVersion>3.0</AssemblyVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<Reference Include="System.Management" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<None Remove="Icons\Cross.png" />
<None Remove="Icons\FondiumU.ico" />
<None Remove="Icons\Open.png" />
</ItemGroup>
<ItemGroup>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<Resource Include="Icons\Cross.png" />
<Resource Include="Icons\FondiumU.ico" />
<Resource Include="Icons\Open.png" />
</ItemGroup>
<ItemGroup>
<None Include="Guis\SieStarterCsvExporter\Convert_SINAMICS_trace_CSV.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="TextCopy" Version="6.2.1" />
<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\RoboSharp\RoboSharp.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,54 +0,0 @@
<Window x:Class="FSI.Lib.Guis.AutoPw.FrmMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FSI.Lib.Guis.AutoPw"
mc:Ignorable="d"
Title="Passwort eingeben"
SizeToContent="WidthAndHeight"
Height="Auto"
Width="Auto"
Icon="../../Icons/FondiumU.ico"
WindowStyle="ToolWindow">
<Grid FocusManager.FocusedElement="{Binding ElementName=TbPw}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Passwort:"
Margin="5 5" />
<PasswordBox x:Name="TbPw"
Margin=" 5 5 5 5"
MinWidth="150"
PasswordChanged="TbPw_PasswordChanged"
KeyDown="TbPw_KeyDown" />
</StackPanel>
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<Button x:Name="BtnOk"
Content="Ok"
MinWidth="75"
Click="BtnOk_Click"
Margin="5 5 " />
<Button x:Name="BtnCancel"
Content="Abbruch"
MinWidth="75"
Click="BtnCancel_Click"
Margin="5 5 " />
</StackPanel>
<StatusBar Grid.Row="2">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text="{Binding CurrentTime}" />
</StatusBarItem>
</StatusBar>
</Grid>
</Window>

View File

@@ -1,91 +0,0 @@
using Microsoft.Extensions.Logging;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace FSI.Lib.Guis.AutoPw
{
/// <summary>
/// Interaktionslogik für FrmMain.xaml
/// </summary>
public partial class FrmMain : Window
{
private Logger _log;
public bool CloseAtLostFocus { get; set; }
public bool PwOk { get; set; }
public FrmMain()
{
InitializeComponent();
_log = LogManager.GetCurrentClassLogger();
BtnOk.IsEnabled = false;
Deactivated += FrmMain_Deactivated;
DataContext = new MVVM.ViewModel.CurrentTimeViewModel();
}
private void FrmMain_Deactivated(object sender, System.EventArgs e)
{
if (CloseAtLostFocus)
Visibility = Visibility.Hidden;
}
private void BtnOk_Click(object sender, RoutedEventArgs e)
{
_log.Info(GetType().Namespace);
Close();
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
PwOk = false;
}
private void TbPw_PasswordChanged(object sender, RoutedEventArgs e)
{
if (((PasswordBox)sender).Password.Equals(DateTime.Now.ToString("yyyyMMdd")))
{
BtnOk.IsEnabled = true;
PwOk = true;
}
else
{
BtnOk.IsEnabled = false;
PwOk = false;
}
}
private void TbPw_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Return && e.Key != Key.Enter)
return;
if (BtnOk.IsEnabled == true)
{
PwOk = true;
}
else
{
PwOk = false;
}
Close();
}
}
}

View File

@@ -1,55 +0,0 @@
<Window x:Class="FSI.Lib.Guis.DeEncryptMessage.FrmMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FSI.Lib.Guis.DeEncryptMessage"
mc:Ignorable="d"
Title="FSI Message Ent-/Verschlüsseln"
SizeToContent="WidthAndHeight"
Height="Auto"
Width="Auto"
Icon="../../Icons/FondiumU.ico">
<Grid MinWidth="300">
<Grid.RowDefinitions>
<RowDefinition MinHeight="100" />
<RowDefinition MinHeight="100" />
<RowDefinition MaxHeight="30"
Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox x:Name="tbInput"
TextWrapping="Wrap"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto" />
<TextBox x:Name="tboutput"
Grid.Row="1"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto" />
<StackPanel Orientation="Horizontal"
Grid.Row="2">
<Button x:Name="btnCrypt"
Content="verschlüsseln"
Margin="5 5 5 5 "
Click="btnCrypt_Click" />
<Button x:Name="btnDeCrypt"
Content="entschlüsseln"
Margin="5 5 5 5 "
Click="btnDeCrypt_Click" />
</StackPanel>
<StatusBar Grid.Row="3">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text="{Binding CurrentTime}" />
</StatusBarItem>
</StatusBar>
</Grid>
</Window>

View File

@@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace FSI.Lib.Guis.DeEncryptMessage
{
/// <summary>
/// Interaktionslogik für FrmMain.xaml
/// </summary>
public partial class FrmMain : Window
{
public string Password { get; set; }
public bool CloseAtLostFocus { get; set; }
public FrmMain()
{
InitializeComponent();
Deactivated += FrmMain_Deactivated;
DataContext = new MVVM.ViewModel.CurrentTimeViewModel();
}
private void FrmMain_Deactivated(object sender, System.EventArgs e)
{
if (CloseAtLostFocus)
Visibility = Visibility.Hidden;
}
private void btnDeCrypt_Click(object sender, RoutedEventArgs e)
{
try
{
tboutput.Text = DeEncryptString.DeEncrypt.DecryptString(tbInput.Text, Password);
}
catch (Exception)
{
MessageBox.Show("Text kann nicht entschlüsselt werden!", "Achtung", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void btnCrypt_Click(object sender, RoutedEventArgs e)
{
try
{
tboutput.Text = DeEncryptString.DeEncrypt.CryptString(tbInput.Text, Password);
}
catch (Exception)
{
MessageBox.Show("Text kann nicht verschlüsselt werden!", "Achtung", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

View File

@@ -1,184 +0,0 @@
<Window x:Class="FSI.Lib.Guis.Folder.Mgt.FrmMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:local="clr-namespace:FSI.Lib.Guis.Folder.Mgt"
xmlns:control="clr-namespace:FSI.Lib.Wpf.Ctrls.FilterDataGrid"
d:DataContext="{d:DesignInstance Type=local:ViewModel}"
mc:Ignorable="d"
SizeToContent="Width"
Height="800"
Width="Auto"
Icon="../../Icons/FondiumU.ico">
<Window.Resources>
<ObjectDataProvider x:Key="FilteredData"></ObjectDataProvider>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Command="{Binding CmdOpen}"
Gesture="CTRL+o" Key="O" />
<KeyBinding Command="{Binding CmdCopyToClip}"
Gesture="CTRL+x" Key="X" />
</Window.InputBindings>
<Grid FocusManager.FocusedElement="{Binding ElementName=tbSearch}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"
Margin="0,10"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
HorizontalAlignment="Left"
Orientation="Horizontal">
<Label Margin="0,0,20,0"
VerticalAlignment="Bottom"
Content="Suche:"
FontWeight="Bold" />
<TextBox Name="tbSearch"
Height="26"
MinWidth="200"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Text="{Binding Search, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="tbSearch_TextChanged" />
<Button Width="Auto"
Margin="5,0,0,0"
Padding="4"
Command="{Binding RefreshCommand}"
ToolTip="Filter löschen"
Cursor="Hand">
<Image Source="../../Icons/Cross.png"
Height="14" />
</Button>
</StackPanel>
<StackPanel Grid.Column="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Width="Auto"
Margin="0 0 20 0"
Padding="4"
ToolTip="öffen"
Command="{Binding CmdOpen}"
IsEnabled="{Binding CanExecuteOpen}"
Cursor="Hand">
<Image Source="../../Icons/Open.png"
Height="14" />
</Button>
</StackPanel>
</Grid>
<StackPanel Grid.Row="1"
HorizontalAlignment="Left"
Orientation="Horizontal"
Margin="0,5">
<Label Margin="0,0,20,0"
VerticalAlignment="Bottom"
Content="Quick-Filter:"
FontWeight="Bold" />
<Button Width="Auto"
Margin="5,0,0,0"
Padding="4"
ToolTip="Filter löschen"
Command="{Binding RefreshCommand}"
Cursor="Hand">
<Image Source="../../Icons/Cross.png"
Height="14" />
</Button>
<Button Content="Alg"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf Alg"
Command="{Binding CmdQuickSearch}"
CommandParameter="Alg"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="PL1"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL1"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL1"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="PL2"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL2"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL2"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="PL3"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL3"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL3"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="SMZ"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf SMZ"
Command="{Binding CmdQuickSearch}"
CommandParameter="SMZ"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
</StackPanel>
<control:FilterDataGrid x:Name="FilterDataGrid"
Grid.Row="2"
AutoGenerateColumns="False"
DateFormatString="d"
FilterLanguage="German"
ItemsSource="{Binding FilteredData, UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Single"
SelectedItem="{Binding SelectedData, UpdateSourceTrigger=PropertyChanged}"
ShowElapsedTime="false"
ShowRowsCount="True"
ShowStatusBar="True">
<control:FilterDataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding CmdOpen}" />
</control:FilterDataGrid.InputBindings>
<control:FilterDataGrid.Columns>
<control:DataGridTemplateColumn FieldName="Plant"
Header="Anlage"
IsColumnFiltered="True"
SortMemberPath="Plant">
<control:DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="local:Interface">
<TextBlock Text="{Binding Plant}" />
</DataTemplate>
</control:DataGridTemplateColumn.CellTemplate>
</control:DataGridTemplateColumn>
<control:DataGridTextColumn Binding="{Binding SubPlant}"
Header="Teilanlagen"
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding Description}"
Header="Beschreibung"
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding Path}"
Header="Pfad"
IsReadOnly="True"
IsColumnFiltered="True" />
</control:FilterDataGrid.Columns>
</control:FilterDataGrid>
</Grid>
</Window>

View File

@@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace FSI.Lib.Guis.Folder.Mgt
{
/// <summary>
/// Interaktionslogik für FrmMain.xaml
/// </summary>
public partial class FrmMain : Window
{
public ViewModel Folder { get; set; }
public bool CloseAtLostFocus { get; set; }
public IEnumerable<IInterface> Data { get; set; }
public FrmMain()
{
InitializeComponent();
Loaded += Main_Loaded;
Deactivated += FrmMain_Deactivated;
}
private void FrmMain_Deactivated(object sender, System.EventArgs e)
{
if (CloseAtLostFocus)
Visibility = Visibility.Hidden;
}
private void Main_Loaded(object sender, RoutedEventArgs e)
{
Title = "FSI Ordner-Auswahl";
Title += " v" + Assembly.GetExecutingAssembly().GetName().Version; // Version in Titel eintragen
Folder = new ViewModel(new DataProvider())
{
InputData = Data
};
DataContext = Folder;
Folder.Load();
}
private void tbSearch_TextChanged(object sender, TextChangedEventArgs e)
{
tbSearch.Select(tbSearch.Text.Length, 0);
}
}
}

View File

@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FSI.Lib.Guis.Folder.Mgt
{
public interface IInterface
{
string Plant { get; set; }
string SubPlant { get; set; }
string Name { get; set; }
string Description { get; set; }
string Path { get; set; }
}
public struct Model : IInterface
{
private string _plant;
private string _subPlant;
private string _name;
private string _description;
private string _path;
public string Plant { get => _plant; set => _plant = value; }
public string SubPlant { get => _subPlant; set => _subPlant = value; }
public string Name { get => _name; set => _name = value; }
public string Description { get => _description; set => _description = value; }
public string Path { get => _path; set => _path = value; }
public string DescriptionDtl { get => _plant + " " + SubPlant + " " + Description; }
}
public interface IDataProvider
{
IEnumerable<Model> Load(IEnumerable<IInterface> datas);
}
}

View File

@@ -1,183 +0,0 @@
using FSI.Lib.MVVM;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
namespace FSI.Lib.Guis.Folder.Mgt
{
public class ViewModel : MVVM.ViewModelBase
{
readonly IDataProvider _dataProvider;
private string _search;
private ICollectionView _collView;
public ICommand RefreshCommand => new DelegateCommand(RefreshData);
private ICommand _cmdQuickSearch;
private ICommand _cmdOpen;
private ICommand _cmdCopyToClip;
public ViewModel(IDataProvider dataProvider)
{
Datas = new ObservableCollection<Model>();
_dataProvider = dataProvider;
_cmdQuickSearch = new RelayCommand<object>(ExecuteQuickSearch, CanExecuteQuickSearch);
_cmdOpen = new RelayCommand<object>(ExecuteOpen, CanExecuteOpen);
_cmdCopyToClip = new RelayCommand<object>(ExecuteCopyToClip, CanExecuteCopyToClip);
}
public ObservableCollection<Model> Datas { get; }
public ObservableCollection<Model> FilteredData { get; set; }
public Model SelectedData { get; set; }
public IEnumerable<IInterface> InputData { get; set; }
public void Load()
{
var datas = _dataProvider.Load(InputData);
Datas.Clear();
if (datas != null)
{
foreach (Model data in datas)
{
Datas.Add(data);
}
}
FilteredData = new ObservableCollection<Model>(Datas);
_collView = CollectionViewSource.GetDefaultView(FilteredData);
}
public string Search
{
get => _search;
set
{
_search = value;
_collView.Filter = e =>
{
var item = (Model)e;
return ((item.Plant?.StartsWith(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (item.SubPlant?.StartsWith(_search, StringComparison.OrdinalIgnoreCase) ?? false)
#if NET472
|| (item.Path?.Contains(_search) ?? false)
|| (item.DescriptionDtl?.Contains(_search) ?? false)
#elif NET6_0
|| (item.Path?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (item.DescriptionDtl?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
#endif
);
};
_collView.Refresh();
FilteredData = new ObservableCollection<Model>(_collView.OfType<Model>().ToList());
OnPropertyChanged();
}
}
public void QuickSearch(string search)
{
Search = search + " ";
}
private void RefreshData(object obj)
{
Search = string.Empty;
}
private bool CanExecuteQuickSearch(object obj)
{
return true;
}
private void ExecuteQuickSearch(object obj)
{
QuickSearch(obj.ToString());
}
public ICommand CmdQuickSearch
{
get { return _cmdQuickSearch; }
set => _cmdQuickSearch = value;
}
private bool CanExecuteOpen(object obj)
{
if (Directory.Exists(SelectedData.Path))
return true;
else
return false;
}
private void ExecuteOpen(object obj)
{
new Process
{
StartInfo = new ProcessStartInfo()
{
FileName = "explorer.exe",
Arguments = SelectedData.Path
}
}.Start();
}
public ICommand CmdOpen
{
get { return _cmdOpen; }
set => _cmdOpen = value;
}
private bool CanExecuteCopyToClip(object obj)
{
if (Directory.Exists(SelectedData.Path))
return true;
else
return false;
}
private void ExecuteCopyToClip(object obj)
{
TextCopy.ClipboardService.SetText(SelectedData.Path);
}
public ICommand CmdCopyToClip
{
get { return _cmdCopyToClip; }
set => _cmdCopyToClip = value;
}
}
public class DataProvider : IDataProvider
{
public IEnumerable<Model> Load(IEnumerable<IInterface> folders)
{
var newDatas = new ObservableCollection<Model>();
foreach (IInterface folder in folders)
{
Model newData = new();
newData.Plant = folder.Plant;
newData.SubPlant = folder.SubPlant;
newData.Name = folder.Name;
newData.Description = folder.Description;
newData.Path = folder.Path;
newDatas.Add(newData);
}
newDatas = new ObservableCollection<Model>(newDatas.OrderBy(i => i.DescriptionDtl));
return newDatas;
}
}
}

View File

@@ -1,9 +0,0 @@
namespace FSI.Lib.Guis.IbaDirSync
{
public interface IInterface
{
public bool AutoStart { get; set; }
public string Source { get; set; }
public string Destination { get; set; }
}
}

View File

@@ -1,76 +0,0 @@
using FSI.Lib.MVVM;
using RoboSharp;
using System.Text;
using System.Windows.Input;
namespace FSI.Lib.Guis.IbaDirSync
{
public class ViewModel : ViewModelBase
{
private ICommand _cmdStart;
private ICommand _cmdStop;
public ViewModel()
{
_cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart);
_cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
RoboCopy = new RoboSharp.RoboCommand();
}
public IInterface Data { get; set; }
public void Init()
{
if (Data.AutoStart)
{
ExecuteStart(null);
}
}
public RoboCommand RoboCopy { get; set; }
public ICommand CmdStart
{
get { return _cmdStart; }
set => _cmdStart = value;
}
private bool CanExecuteStop(object obj)
{
return RoboCopy.IsRunning;
}
private void ExecuteStop(object obj)
{
if (RoboCopy != null)
{
RoboCopy.Stop();
// RoboCopy.Dispose();
}
}
public ICommand CmdStop
{
get { return _cmdStop; }
set => _cmdStop = value;
}
private bool CanExecuteStart(object obj)
{
return !RoboCopy.IsRunning;
}
private void ExecuteStart(object obj)
{
RoboCopy.CopyOptions.Source = Data.Source;
RoboCopy.CopyOptions.Destination = Data.Destination;
RoboCopy.CopyOptions.CopySubdirectories = true;
RoboCopy.CopyOptions.MonitorSourceChangesLimit = 1;
RoboCopy.StopIfDisposing = true;
RoboCopy.Start();
}
}
}

View File

@@ -1,116 +0,0 @@
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace FSI.Lib.Guis.Pdf.Mgt
{
public class Cmds
{
public static Task DelDirectoriesAsync(string path)
{
return Task.Factory.StartNew(() =>
{
Action<string> DelPath = null;
DelPath = p =>
{
Directory.EnumerateFiles(p).ToList().ForEach(System.IO.File.Delete); // Dateien im Verzeichnis löschen
Directory.EnumerateDirectories(p).ToList().ForEach(DelPath);
Directory.EnumerateDirectories(p).ToList().ForEach(Directory.Delete); // Verzeichnis löschen
};
if (Directory.Exists(path))
{
string[] directories = Directory.GetDirectories(path); // Alle Unterverzeichniss auslesen
foreach (string directory in directories)
{
if (!(directory.Replace(path, "")).StartsWith(".")) // Überprüfen ob Verzeichnis mit "." startet
{
DelPath(directory); // Unterverzeichnisse inkl. Dateien löschen
Directory.Delete(directory); // Verzeichnis löschen
}
}
}
});
}
public static Task DelFilesAsync(string path)
{
return Task.Factory.StartNew(() =>
{
var files = Directory.GetFiles(path);
foreach (string file in files)
{
FileInfo fileInfo = new FileInfo(file);
const int STR_LENGTH = 27; // min. Dateinamen Länge (Zahlen inkl. Bindestriche)
if (!fileInfo.Name.StartsWith(".")) // Überprüfen ob Datei mit "." startet
{
if (fileInfo.Extension != ".pdf" || fileInfo.Name.Length <= STR_LENGTH) // Überprüfen ob es sich um eine PDF-Datei handelt
{
System.IO.File.Delete(fileInfo.FullName); // ... wenn nicht Datei löschen
}
else
{
string[] fileStrings = fileInfo.Name.Substring(0, STR_LENGTH - 1).Split('-'); // Zahlenblock splitten für Überprüfung
if (fileStrings.Length == 3) // 3 Zahlenblöcke vorhanden?
{
if (fileStrings[0].Length != 10 || !Int64.TryParse(fileStrings[0], out _) || fileStrings[1].Length != 10 || !Int64.TryParse(fileStrings[1], out _) || fileStrings[2].Length != 4 || !Int64.TryParse(fileStrings[2], out _)) // Länge der Zahlenblöcke überprüfen
{
System.IO.File.Delete(fileInfo.FullName); // ..., wenn Länge nicht passt, Datei löschen
}
}
else
{
System.IO.File.Delete(fileInfo.FullName); // ..., wenn nicht Datei löschen
}
}
}
}
});
}
public static Task CreatePdfShortcutsAsync(string path)
{
return Task.Factory.StartNew(() =>
{
var files = Directory.GetFiles(path, "*.pdf"); // alle PDF-Dateien einlesen
foreach (var file in files)
{
FileInfo fileInfo = new FileInfo(file);
string[] fileStrings = System.IO.Path.GetFileNameWithoutExtension(fileInfo.Name).Split(' '); // Datei-Namen für Verzeichnis Bezeichnung aufteilen
string tmpPath = path + fileStrings[1] + " " + fileStrings[2]; // Verzeichnis Bezeichnung zusammenstellen
if (Convert.ToBoolean(ConfigurationManager.AppSettings["SubDir"])) // mit/ohne Unterverzeichnis
{
tmpPath = path + fileStrings[1] + @"\" + fileStrings[2]; // mit Unterverzeichnis
}
else
{
tmpPath = path + fileStrings[1] + " " + fileStrings[2]; // ohne Unterverzeichnis
}
Directory.CreateDirectory(tmpPath); // Verzeichnis erstellen
// Shortcut erstellen
if (Convert.ToBoolean(ConfigurationManager.AppSettings["WithNo"]))
{
LnkParser.ShortCut.Create(System.IO.Path.GetFileNameWithoutExtension(tmpPath + "\\" + fileInfo.Name), tmpPath, fileInfo.FullName, System.IO.Path.GetFileNameWithoutExtension(fileInfo.FullName));
}
else
{
LnkParser.ShortCut.Create(System.IO.Path.GetFileNameWithoutExtension(fileInfo.Name.Replace(fileStrings[0], "")), tmpPath, fileInfo.FullName, System.IO.Path.GetFileNameWithoutExtension(fileInfo.FullName));
}
}
});
}
}
}

View File

@@ -1,68 +0,0 @@
<Window x:Class="FSI.Lib.Guis.Pdf.Mgt.FrmMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FSI.Lib.Guis.Pdf.Mgt"
mc:Ignorable="d"
Title="FSI Eplan PDF Mgt"
SizeToContent="WidthAndHeight"
Height="Auto"
Width="Auto"
Icon="../../Icons/FondiumU.ico"
WindowStyle="ToolWindow">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="30"
Height="*" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button x:Name="btnCleanUp"
Content="aufräumen"
Margin="5 5 5 5 "
Click="btnCleanUp_Click" />
<Button x:Name="btnCreateShortcuts"
Content="Verknüpfungen erstellen"
Margin="5 5 5 5 "
Click="btnCreateShortcuts_Click" />
<Button x:Name="btnStart"
Content="alles ausführen"
Margin="5 5 5 5 "
Click="btnStart_Click" />
</StackPanel>
<StatusBar Grid.Row="2">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock x:Name="tbStatus" />
</StatusBarItem>
<StatusBarItem Grid.Column="1">
<ProgressBar x:Name="pgProgress"
Width="80"
Height="18" />
</StatusBarItem>
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text="{Binding CurrentTime}" />
</StatusBarItem>
</StatusBar>
</Grid>
</Window>

View File

@@ -1,71 +0,0 @@
using System.Configuration;
using System.Reflection;
using System.Windows;
namespace FSI.Lib.Guis.Pdf.Mgt
{
/// <summary>
/// Interaktionslogik für FrmMain.xaml
/// </summary>
public partial class FrmMain : Window
{
public bool CloseAtLostFocus { get; set; }
public FrmMain()
{
InitializeComponent();
DataContext = new MVVM.ViewModel.CurrentTimeViewModel();
Title += " v" + Assembly.GetExecutingAssembly().GetName().Version; // Version in Titel eintragen
Deactivated += FrmMain_Deactivated;
}
private void FrmMain_Deactivated(object sender, System.EventArgs e)
{
if (CloseAtLostFocus)
Visibility = Visibility.Hidden;
}
private async void btnCleanUp_Click(object sender, RoutedEventArgs e)
{
BtnMgt(false); // Schaltflächen sperren
await Cmds.DelDirectoriesAsync(ConfigurationManager.AppSettings["PdfPath"]); // nicht benötigte/zulässige Verzeichnisse löschen
await Cmds.DelFilesAsync(ConfigurationManager.AppSettings["PdfPath"]); // nicht benötigte/zulässige Datien löschen
BtnMgt(true); // Schaltflächen freigeben
}
private async void btnCreateShortcuts_Click(object sender, RoutedEventArgs e)
{
BtnMgt(false); // Schaltflächen sperren
await Cmds.CreatePdfShortcutsAsync(ConfigurationManager.AppSettings["PdfPath"]); // Verzeichnisstruktur und Verknüpfungen erstellen
BtnMgt(true); // Schaltflächen freigeben
}
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
BtnMgt(false); // Schaltflächen sperren
await Cmds.DelDirectoriesAsync(ConfigurationManager.AppSettings["PdfPath"]); // nicht benötigte/zulässige Verzeichnisse löschen
await Cmds.DelFilesAsync(ConfigurationManager.AppSettings["PdfPath"]); // nicht benötigte/zulässige Datien lösche
await Cmds.CreatePdfShortcutsAsync(ConfigurationManager.AppSettings["PdfPath"]); // Verzeichnisstruktur und Verknüpfungen erstellen
BtnMgt(true); // Schaltflächen freigeben
}
private void BtnMgt(bool enable)
{
btnCleanUp.IsEnabled =
btnCreateShortcuts.IsEnabled =
btnStart.IsEnabled = enable;
pgProgress.IsIndeterminate = !enable;
// Status anzeige
if (enable)
{
tbStatus.Text = "beendet";
}
else
{
tbStatus.Text = "gestartet";
}
}
}
}

View File

@@ -1,185 +0,0 @@
<Window x:Class="FSI.Lib.Guis.Prj.Mgt.FrmMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:local="clr-namespace:FSI.Lib.Guis.Prj.Mgt"
xmlns:control="clr-namespace:FSI.Lib.Wpf.Ctrls.FilterDataGrid"
xmlns:viewmodel="clr-namespace:FSI.Lib.Guis.Prj.Mgt.ViewModel"
d:DataContext="{d:DesignInstance Type=viewmodel:ViewModelPrj}"
mc:Ignorable="d"
SizeToContent="Width"
Height="800"
Width="Auto"
Icon="../../Icons/FondiumU.ico">
<Window.Resources>
<ObjectDataProvider x:Key="PrjsFiltered"></ObjectDataProvider>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Command="{Binding CmdOpen}"
Gesture="CTRL+o" />
</Window.InputBindings>
<Grid FocusManager.FocusedElement="{Binding ElementName=tbSearch}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"
Margin="0,10"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
HorizontalAlignment="Left"
Orientation="Horizontal">
<Label Margin="0,0,20,0"
VerticalAlignment="Bottom"
Content="Suche:"
FontWeight="Bold" />
<TextBox Name="tbSearch"
Height="26"
MinWidth="200"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Text="{Binding Search, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="tbSearch_TextChanged" />
<Button Width="Auto"
Margin="5,0,0,0"
Padding="4"
Command="{Binding RefreshCommand}"
ToolTip="Filter löschen"
Cursor="Hand">
<Image Source="../../Icons/Cross.png"
Height="14" />
</Button>
</StackPanel>
<StackPanel Grid.Column="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Width="Auto"
Margin="0 0 20 0"
Padding="4"
ToolTip="öffen"
Command="{Binding CmdOpen}"
IsEnabled="{Binding CanExecuteOpen}"
Cursor="Hand">
<Image Source="../../Icons/Open.png"
Height="14" />
</Button>
</StackPanel>
</Grid>
<StackPanel Grid.Row="1"
HorizontalAlignment="Left"
Orientation="Horizontal"
Margin="0,5">
<Label Margin="0,0,20,0"
VerticalAlignment="Bottom"
Content="Quick-Filter:"
FontWeight="Bold" />
<Button Width="Auto"
Margin="5,0,0,0"
Padding="4"
ToolTip="Filter löschen"
Command="{Binding RefreshCommand}"
Cursor="Hand">
<Image Source="../../Icons/Cross.png"
Height="14" />
</Button>
<Button Content="PL1"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL1"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL1"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="PL2"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL2"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL2"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="PL3"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf PL3"
Command="{Binding CmdQuickSearch}"
CommandParameter="PL3"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
<Button Content="SMZ"
Width="Auto"
Margin="10,0,0,0"
ToolTip="Filter auf SMZ"
Command="{Binding CmdQuickSearch}"
CommandParameter="SMZ"
FocusManager.FocusedElement="{Binding ElementName=tbSearch}" />
</StackPanel>
<control:FilterDataGrid x:Name="FilterDataGrid"
Grid.Row="2"
AutoGenerateColumns="False"
DateFormatString="d"
FilterLanguage="German"
ItemsSource="{Binding PrjsFiltered, UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Single"
SelectedItem="{Binding SeletctedPrj, UpdateSourceTrigger=PropertyChanged}"
ShowElapsedTime="false"
ShowRowsCount="True"
ShowStatusBar="True">
<control:FilterDataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding CmdOpen}" />
</control:FilterDataGrid.InputBindings>
<control:FilterDataGrid.Columns>
<control:DataGridTemplateColumn FieldName="PlantNo"
Header="Anlagen-Nr."
IsColumnFiltered="True"
SortMemberPath="PlantNo">
<control:DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="local:Prj">
<TextBlock Text="{Binding PlantNo}" />
</DataTemplate>
</control:DataGridTemplateColumn.CellTemplate>
</control:DataGridTemplateColumn>
<control:DataGridTextColumn Binding="{Binding SubPlantNo}"
Header="Teilanlagen-Nr."
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding No, StringFormat={}{0:0000}}"
Header="Lfd.-Nr."
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding Plant}"
Header="Anlage"
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding SubPlant}"
Header="Teilanlage"
IsReadOnly="True"
IsColumnFiltered="True" />
<control:DataGridTextColumn Binding="{Binding Description}"
Header="Bezeichnung"
IsReadOnly="True"
IsColumnFiltered="True" />
</control:FilterDataGrid.Columns>
</control:FilterDataGrid>
</Grid>
</Window>

Some files were not shown because too many files have changed in this diff Show More