We notice that inside of our .Net application we have contention when it comes to using SqlDataReader. While we understand that SqlDataReader is not ThreadSafe, it should scale. The following code is a simple example to show that we cannot scale our application because there is contention on the SqlDataReader GetValue method. We are not bound by CPU, Disk, or Network; Just the internal contention on the SqlDataReader. We can run the application 10 times with 1 thread and it scales linearly, but 10 threads in 1 app does not scale. Any thoughts on how to scale reading from SQL Server in a single c# application?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
namespace ThreadAndSQLTester
{
class Host
{
/// <summary>
/// Gets or sets the receive workers.
/// </summary>
/// <value>The receive workers.</value>
internal List<Worker> Workers { get; set; }
/// <summary>
/// Gets or sets the receive threads.
/// </summary>
/// <value>The receive threads.</value>
internal List<Thread> Threads { get; set; }
public int NumberOfThreads { get; set; }
public int Sleep { get; set; }
public int MinutesToRun { get; set; }
public bool IsRunning { get; set; }
private System.Timers.Timer runTime;
private object lockVar = new object();
public Host()
{
Init(1, 0, 0);
}
public Host(int numberOfThreads, int sleep, int minutesToRun)
{
Init(numberOfThreads, sleep, minutesToRun);
}
private void Init(int numberOfThreads, int sleep, int minutesToRun)
{
this.Workers = new List<Worker>();
this.Threads = new List<Thread>();
this.NumberOfThreads = numberOfThreads;
this.Sleep = sleep;
this.MinutesToRun = minutesToRun;
SetUpTimer();
}
private void SetUpTimer()
{
if (this.MinutesToRun > 0)
{
this.runTime = new System.Timers.Timer();
this.runTime.Interval = TimeSpan.FromMinutes(this.MinutesToRun).TotalMilliseconds;
this.runTime.Elapsed += new System.Timers.ElapsedEventHandler(runTime_Elapsed);
this.runTime.Start();
}
}
void runTime_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.runTime.Stop();
this.Stop();
this.IsRunning = false;
}
public void Start()
{
this.IsRunning = true;
Random r = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < this.NumberOfThreads; i++)
{
string threadPoolId = Math.Ceiling(r.NextDouble() * 10).ToString();
Worker worker = new Worker("-" + threadPoolId); //i.ToString());
worker.Sleep = this.Sleep;
this.Workers.Add(worker);
Thread thread = new Thread(worker.Work);
worker.Name = string.Format("WorkerThread-{0}", i);
thread.Name = worker.Name;
this.Threads.Add(thread);
thread.Start();
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Started new Worker Thread. Total active: {0}", i + 1));
}
}
public void Stop()
{
if (this.Workers != null)
{
lock (lockVar)
{
for (int i = 0; i < this.Workers.Count; i++)
{
//Thread thread = this.Threads[i];
//thread.Interrupt();
this.Workers[i].IsEnabled = false;
}
for (int i = this.Workers.Count - 1; i >= 0; i--)
{
Worker worker = this.Workers[i];
while (worker.IsRunning)
{
Thread.Sleep(32);
}
}
foreach (Thread thread in this.Threads)
{
thread.Abort();
}
this.Workers.Clear();
this.Threads.Clear();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Threading;
using System.ComponentModel;
using System.Data.OleDb;
namespace ThreadAndSQLTester
{
class Worker
{
public bool IsEnabled { get; set; }
public bool IsRunning { get; set; }
public string Name { get; set; }
public int Sleep { get; set; }
private string dataCnString { get; set; }
private string logCnString { get; set; }
private List<Log> Logs { get; set; }
public Worker(string threadPoolId)
{
this.Logs = new List<Log>();
SqlConnectionStringBuilder cnBldr = new SqlConnectionStringBuilder();
cnBldr.DataSource = #"trgcrmqa3";
cnBldr.InitialCatalog = "Scratch";
cnBldr.IntegratedSecurity = true;
cnBldr.MultipleActiveResultSets = true;
cnBldr.Pooling = true;
dataCnString = GetConnectionStringWithWorkStationId(cnBldr.ToString(), threadPoolId);
cnBldr = new SqlConnectionStringBuilder();
cnBldr.DataSource = #"trgcrmqa3";
cnBldr.InitialCatalog = "Scratch";
cnBldr.IntegratedSecurity = true;
logCnString = GetConnectionStringWithWorkStationId(cnBldr.ToString(), string.Empty);
IsEnabled = true;
}
private string machineName { get; set; }
private string GetConnectionStringWithWorkStationId(string connectionString, string connectionPoolToken)
{
if (string.IsNullOrEmpty(machineName)) machineName = Environment.MachineName;
SqlConnectionStringBuilder cnbdlr;
try
{
cnbdlr = new SqlConnectionStringBuilder(connectionString);
}
catch
{
throw new ArgumentException("connection string was an invalid format");
}
cnbdlr.WorkstationID = machineName + connectionPoolToken;
return cnbdlr.ConnectionString;
}
public void Work()
{
int i = 0;
while (this.IsEnabled)
{
this.IsRunning = true;
try
{
Log log = new Log();
log.WorkItemId = Guid.NewGuid();
log.StartTime = DateTime.Now;
List<object> lst = new List<object>();
using (SqlConnection cn = new SqlConnection(this.dataCnString))
{
try
{
cn.Open();
using (SqlCommand cmd = new SqlCommand("Analysis.spSelectTestData", cn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) // DBHelper.ExecuteReader(cn, cmd))
{
while (dr.Read())
{
CreateClaimHeader2(dr, lst);
}
dr.Close();
}
cmd.Cancel();
}
}
catch { }
finally
{
cn.Close();
}
}
log.StopTime = DateTime.Now;
log.RouteName = this.Name;
log.HostName = this.machineName;
this.Logs.Add(log);
i++;
if (i > 1000)
{
Console.WriteLine(string.Format("Thread: {0} executed {1} items.", this.Name, i));
i = 0;
}
if (this.Sleep > 0) Thread.Sleep(this.Sleep);
}
catch { }
}
this.LogMessages();
this.IsRunning = false;
}
private void CreateClaimHeader2(IDataReader reader, List<object> lst)
{
lst.Add(reader["ClaimHeaderID"]);
lst.Add(reader["ClientCode"]);
lst.Add(reader["MemberID"]);
lst.Add(reader["ProviderID"]);
lst.Add(reader["ClaimNumber"]);
lst.Add(reader["PatientAcctNumber"]);
lst.Add(reader["Source"]);
lst.Add(reader["SourceID"]);
lst.Add(reader["TotalPayAmount"]);
lst.Add(reader["TotalBillAmount"]);
lst.Add(reader["FirstDateOfService"]);
lst.Add(reader["LastDateOfService"]);
lst.Add(reader["MaxStartDateOfService"]);
lst.Add(reader["MaxValidStartDateOfService"]);
lst.Add(reader["LastUpdated"]);
lst.Add(reader["UpdatedBy"]);
}
/// <summary>
/// Toes the data table.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data">The data.</param>
/// <returns></returns>
public DataTable ToDataTable<T>(IEnumerable<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
if (props == null) throw new ArgumentNullException("Table properties.");
if (data == null) throw new ArgumentNullException("data");
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item) ?? DBNull.Value;
}
table.Rows.Add(values);
}
return table;
}
private void LogMessages()
{
using (SqlConnection cn = new SqlConnection(this.logCnString))
{
try
{
cn.Open();
DataTable dt = ToDataTable(this.Logs);
Console.WriteLine(string.Format("Logging {0} records for Thread: {1}", this.Logs.Count, this.Name));
using (SqlCommand cmd = new SqlCommand("Analysis.spInsertWorkItemRouteLog", cn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#dt", dt);
cmd.ExecuteNonQuery();
}
Console.WriteLine(string.Format("Logged {0} records for Thread: {1}", this.Logs.Count, this.Name));
}
finally
{
cn.Close();
}
}
}
}
}
1.A DataReader works in a connected environment,
whereas DataSet works in a disconnected environment.
2.A DataSet represents an in-memory cache of data consisting of any number of inter related DataTable objects. A DataTable object represents a tabular block of in-memory data.
SqlDataAdapter or sqlDataReader
Difference between SqlDataAdapter or sqlDataReader ?
Ans : 1.A DataReader works in a connected environment,
whereas DataSet works in a disconnected environment.
2.A DataSet represents an in-memory cache of data consisting of any number of inter related DataTable objects. A DataTable object represents a tabular block of in-memory data.
SqlDataAdapter or sqlDataReader
Related
I cant seem to find the problem on the code but it the server is not displaying anything. It displays the catch. Client seems find and it sets the players name and score and sends it but I cant seem to find the issue on this one why server is not displaying the name and score.
Here is my Client:
public class Client
{
private static final int BUFSIZE = 64;
public static void main(String[] args) throws IOException
{
try
{
int scores;
Scanner in = new Scanner(System.in);
Socket clntSock = new Socket("127.0.0.1", 6000);
System.out.println("What is the filename?");
String input = in.nextLine();
Scanner fileInput = new Scanner(new File(input));
ObjectOutputStream out = new
ObjectOutputStream(clntSock.getOutputStream());
Player playerObject = new Player();
playerObject.setName(fileInput.nextLine());
System.out.println(""+playerObject.getName());
for (int i = 0; i < 5; i++)
{
scores = Integer.parseInt(fileInput.nextLine());
playerObject.setScore(scores);
System.out.println(""+playerObject.getScores().get(i));
}
out.writeObject(playerObject);
in.close();
fileInput.close();
out.close();
clntSock.close();
}
catch ( UnknownHostException ex )
{
System.out.println( "Unknown host" );
}
}
}
and my Host:
public class Host
{
private static final int BUFSIZE = 64;
public static void main(String[] args) throws IOException
{
// Step 1: Create a ServerSocket.
ServerSocket servSock = new ServerSocket(6000);
PrintStream fileOut = new PrintStream("Datafromclient.txt");
try
{
// Step 2: Wait for a connection..
Socket clntSock = servSock.accept();
// Step 3: Get input and output streams.
System.out.println("Step 3: Get object input stream.,");
ObjectInputStream objectIn = new
ObjectInputStream(clntSock.getInputStream());
Player playerObjct = (Player)objectIn.readObject();
System.out.println("The name of Player: "+playerObjct.getName());
for(int i=0; i <5; i++)
{
System.out.println("Scores:"+playerObjct.getScores().get(i));
}
objectIn.close();
clntSock.close();
// Step 5: Close connection
objectIn.close();
clntSock.close();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
System.err.println(e);
}
}
}
My player class:
public class Player
private String name;
private int playerId;
private int bestScore;
private static int numberOfPlayers = 0;
private ArrayList<Integer> scores = new ArrayList<Integer>();
/* -------------- CONSTRUCTOR --------------------------------------
*/
public Player()
{
numberOfPlayers++;
playerId = numberOfPlayers;
}
public Player(String name)
{
this.name = name;
}
//Create set method for setName
public void setName(String name)
{
this.name = name;
}
//Create set method for setScores
public void setScore(int score)
{
scores.add(score);
}
//Create get method for getPlayerId
public int getPlayerId()
{
return this.playerId;
}
//Create get method for getName
public String getName()
{
return this.name;
}
//Create get method for getScores
public ArrayList<Integer> getScores()
{
return scores;
}
//Create get method for getBestScore
public int getBestScore()
{
calculateBestScore();
return bestScore;
}
//Method to expose the value of numberOfPlayers
public static int getNumberOfPlayers()
{
return numberOfPlayers;
}
//Create get method for calcualteAverage
public double calculateAverage()
{
Integer sum = 0;
if(!scores.isEmpty())
{
for(Integer score : scores)
{
sum += score;
}
return sum.doubleValue() / scores.size();
}
return sum;
}
public void calculateBestScore()
{
bestScore = Collections.max(scores);
}
}
I was missing
import java.io.Serializable;
public class Player implements Serializable
now its working.
Please let me know below method invocation is thread safe or not.
I am calling ThreadStartMain on my main thread and create new threads and invoke A_GetCounryName method on new instance.
Since i am always calling via new instance i think this is thread safe even though i am having instance variables in some classes.
class MyThread
{
private void ThreadStartMain()
{
for (int i = 0; i < 5; i++)
{
A a = new A();
ThreadStart start = new ThreadStart(a.A_GetCounryName);
Thread t = new Thread(start);
t.Start();
}
}
}
class A
{
public B GetNewObject()
{
B bObj = new B();
return bObj;
}
public void A_GetCounryName()
{
B b=GetObject();
string cName=b.B_GetCoutryName();
}
}
class B
{
C cObj = null;
public B()
{
cObj = new C();
cObj.Prop1 = 1;
cObj.Prop1 = 2;
cObj.Prop1 = 3;
}
public string B_GetCoutryName()
{
string countryName= cObj.C_GetCoutryName();
return countryName;
}
}
class C
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Prop3 { get; set; }
public string C_GetCoutryName()
{
string name = "Italy";
return name;
}
}
Yes, this is safe because your threads do not share state. More precisely: They do not access common storage locations.
I am using entlib 5.0.1. I have created a cache using code configuration
IContainerConfigurator configurator =
new UnityContainerConfigurator(_unityContainer);
configurator.ConfigureCache(item.PartitionName, item.MaxItemNumber);
CacheBlockImp block = new CacheBlockImp(
_unityContainer.Resolve<ICacheManager>(item.PartitionName),
item.PartitionType);
I saw a strange behavior with the size limit, I have configured the cache to hold 15 items, I am adding in a loop 18 items, I would suspect afer the addition to have only 15 items, but I get 8, when I have added a refresh action - to be notify when 1 item was evacuated, I really see that 7 of them were evacuated, all the items have the same priority.
class Program
{
static void Main(string[] args)
{
var unityContainer = new UnityContainer();
IContainerConfigurator configurator = new UnityContainerConfigurator(unityContainer);
configurator.ConfigureCache("Test", 10);
var cache = unityContainer.Resolve<ICacheManager>("Test");
for (int i = 0; i < 18; i++)
{
var dummy = new Dummy()
{
ID = i,
Data = "hello " + i.ToString()
};
cache.Add(i.ToString(), dummy, CacheItemPriority.Normal, null, null);
}
Thread.Sleep(1000);
int count = cache.CachedItems().Count;
}
public class Dummy
{
public int ID { get; set; }
public string Data { get; set; }
}
}
public static class CacheBlockExtension
{
public static void ConfigureCache(this IContainerConfigurator configurator, string configKey,
int maxNumOfItems)
{
ConfigurationSourceBuilder builder = new ConfigurationSourceBuilder();
DictionaryConfigurationSource configSource = new DictionaryConfigurationSource();
// simple inmemory cache configuration
builder.ConfigureCaching().ForCacheManagerNamed(configKey).WithOptions
.StartScavengingAfterItemCount(maxNumOfItems)
.StoreInMemory();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.ConfigureContainer(configurator, configSource);
}
public static List<object> CachedItems(this ICacheManager cachemanger)
{
Microsoft.Practices.EnterpriseLibrary.Caching.Cache cache =
(Microsoft.Practices.EnterpriseLibrary.Caching.Cache)cachemanger.GetType().GetField("realCache", System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).GetValue(cachemanger);
List<object> tmpret = new List<object>();
foreach (DictionaryEntry Item in cache.CurrentCacheState)
{
Object key = Item.Key;
CacheItem cacheItem = (CacheItem)Item.Value;
tmpret.Add(cacheItem.Value);
}
return tmpret;
}
I am using FileSystemWatcher to monitor any changes in the app.config file. And also, writing to the config file.
Here is my code :
MyApplication is the main project and DataCache is a clas library referenced in MyApplication.
using System;
using System.Threading;
using System.IO;
namespace MyApplication
{
public class Program
{
public static string rootFolderPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
public static string configFilePath = Path.Combine(rootFolderPath, "MyApplication.exe.config");static void Main(string[] args)
{
//First Time initializing the config file.
ConfigFileMonitor.SetConfigFileAtRuntime(configFilePath);
//Initializing the config file monitor on a separate thread.
Thread monitorThread = new Thread(ConfigFileMonitor.BeginConfigFilesMonitor);
monitorThread.Start();
WriteToConfigFile();
}
static void WriteToConfigFile()
{
Console.WriteLine(String.Format("Hello {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
string _firstName, _lastName = string.Empty;
do
{
Console.WriteLine("");
Console.Write("First Name : ");
_firstName = Console.ReadLine();
Console.Write("Last Name : ");
_lastName = Console.ReadLine();
if(_firstName.Length>0)
DataCache.Section1Data.FirstNameString = _firstName;
if(_lastName.Length>0)
DataCache.Section1Data.LastNameString = _lastName;
Console.WriteLine(String.Format("Hello {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
}
while (true);
}
}
}
using System;
using System.IO;
namespace MyApplication
{
/// <summary>
/// Monitors for any change in the app.config file
/// </summary>
public class ConfigFileMonitor
{
private static FileSystemWatcher _watcher;
private static string _configFilePath = String.Empty;
private static string _configFileName = String.Empty;
/// <summary>
/// Texts the files surveillance.
/// </summary>
public static void BeginConfigFilesMonitor()
{
try
{
string fileToMonitor = Program.configFilePath;
if (fileToMonitor.Length == 0)
Console.WriteLine("Incorrect config file specified to watch");
else
{
_configFileName = Path.GetFileName(fileToMonitor);
_configFilePath = fileToMonitor.Substring(0, fileToMonitor.IndexOf(_configFileName));
}
// Use FileWatcher to check and update only modified text files.
WatchConfigFiles(_configFilePath, _configFileName);
}
catch (Exception e1)
{
Console.WriteLine(e1.Message);
}
}
/// <summary>
/// Watches the files.
/// </summary>
/// <param name="targetDir">The target dir.</param>
/// <param name="filteredBy">The filtered by.</param>
static void WatchConfigFiles(string targetDir, string filteredBy)
{
try
{
_watcher = new FileSystemWatcher();
_watcher.Path = targetDir;
_watcher.Filter = filteredBy;
_watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
_watcher.EnableRaisingEvents = true;
_watcher.Changed += new FileSystemEventHandler(FileChanged);
_watcher.WaitForChanged(WatcherChangeTypes.Changed);
}
catch (Exception e1)
{
Console.WriteLine(e1.Message);
}
}
/// <summary>
/// Handles the Changed event of the File control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
protected static void FileChanged(object sender, FileSystemEventArgs e)
{
try
{
_watcher.EnableRaisingEvents = false;
string filechange = e.FullPath;
Console.WriteLine("Configuration File: " + filechange + "changed");
//Since the file is changed - We have reload the configuration settings again.
SetConfigFileAtRuntime(Path.Combine(Program.rootFolderPath, "MyApplication.exe.config"));
Console.WriteLine(String.Format("New Name : {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
_watcher.EnableRaisingEvents = true;
}
catch (Exception e1)
{
Console.WriteLine(e1.Message);
}
}
/// <summary>
/// Sets the config file at runtime.
/// </summary>
/// <param name="configFilePath"></param>
public static void SetConfigFileAtRuntime(string configFilePath)
{
string runtimeconfigfile;
try
{
if (configFilePath.Length == 0)
Console.WriteLine("Please specify a config file to read from ");
else
{
runtimeconfigfile = configFilePath;
_configFileName = Path.GetFileName(configFilePath);
_configFilePath = configFilePath.Substring(0, configFilePath.IndexOf(_configFileName));
}
// Specify config settings at runtime.
//System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
DataCache.DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(configFilePath);
//Similarly you can apply for other sections like SMTP/System.Net/System.Web etc..
//But you have to set the File Path for each of these
//config.AppSettings.File = runtimeconfigfile;
//This doesn't actually going to overwrite you Exe App.Config file.
//Just refreshing the content in the memory.
DataCache.DataConfigSection.config.Save(System.Configuration.ConfigurationSaveMode.Modified);
//Refreshing Config Section
//ConfigurationManager.RefreshSection("appSettings");
System.Configuration.ConfigurationManager.RefreshSection("MySectionGroup/Section1");
DataCache.Section1Data.configSection1 = null;
}
catch (Exception e1)
{
Console.WriteLine(e1.Message);
}
}
}
}
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="MySectionGroup">
<section name="Section1" type="DataCache.DataConfigSection, DataCache" allowLocation="false" allowDefinition="Everywhere"/>
<section name="Section2" type="DataCache.DataConfigSection, DataCache" allowLocation="false" allowDefinition="Everywhere"/>
</sectionGroup>
</configSections>
<MySectionGroup>
<Section1>
<name
firstName ="Pierce"
lastName ="Brosnan"
/>
</Section1>
</MySectionGroup>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
Here are the DataCache classes :
using System;
namespace DataCache
{
public sealed class DataConfigSection : System.Configuration.ConfigurationSection
{
public static System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));
// Create a "name" element.
[System.Configuration.ConfigurationProperty("name")]
public NameElement Name
{
get
{
return (NameElement)this["name"];
}
set
{
this["name"] = value;
}
}
// Define the "name" element
// with firstName, secondName attributes.
public class NameElement : System.Configuration.ConfigurationElement
{
[System.Configuration.ConfigurationProperty("firstName", DefaultValue = "abcd", IsRequired = true)]
public String FirstName
{
get
{
return (String)this["firstName"];
}
set
{
this["firstName"] = value;
}
}
[System.Configuration.ConfigurationProperty("lastName", DefaultValue = "xyz", IsRequired = true)]
public String LastName
{
get
{
return (String)this["lastName"];
}
set
{
this["lastName"] = value;
}
}
}
}
}
namespace DataCache
{
public class Section1Data
{
public static volatile DataConfigSection configSection1;
private static object syncRoot = new System.Object();
public const string Section1ConfigSectionName = "MySectionGroup/Section1";
private Section1Data() { }
public static DataConfigSection ConfigSection1
{
get
{
lock (syncRoot)
{
if (configSection1 == null)
{
DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));
configSection1 = (DataConfigSection)DataConfigSection.config.GetSection(Section1ConfigSectionName);
}
}
return configSection1;
}
}
public static string FirstNameString
{
get
{
return ConfigSection1.Name.FirstName.ToString();
}
set
{
ConfigSection1.Name.FirstName = value;
DataConfigSection.config.Save();
}
}
public static string LastNameString
{
get
{
return ConfigSection1.Name.LastName.ToString();
}
set
{
ConfigSection1.Name.LastName = value;
DataConfigSection.config.Save();
}
}
}
}
The issue is if there is any change made to the name from console input, FileChanged gets called once and subsequent changes do not fire it. Moreover, if changes are made manually to the config file, it works fine till and events keep firing till a change is made from console.
I am not sure why is this behaving weird. Can anyone help me here.
Thanks,
Monica
Short version would be:
this.Watcher = new FileSystemWatcher();
this.Watcher.Path = this.Dir;
this.Watcher.Filter = this.File;
this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.Watcher.EnableRaisingEvents = true;
this.Watcher.Changed += this.OnFileChange;
However, here is a bit more complex (and ugly) sample, taken out from the live source in which some additional processing (besides just reading the file) is required.
public partial class FileModule
{
private ConcurrentDictionary<string, InputFileInfo> inputFileList = new ConcurrentDictionary<string, InputFileInfo>();
public FileModule()
{
this.InitializeInputFileWatchers();
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public void EnableOrDisableRaisingEventsForFileWatchers(bool enable)
{
foreach (var el in this.inputFileList)
{
el.Value.Watcher.EnableRaisingEvents = enable;
}
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void InitializeInputFileWatchers()
{
for (int i = 0; i < this.InputFiles.Count; i++)
{
if (File.Exists(this.InputFiles[i]))
{
InputFileInfo info = new InputFileInfo();
info.Fullpath = ((FileModuleSettings)this.Settings).InputFiles[i];
info.Watcher.Changed += this.OnFileChange;
this.inputFileList.AddOrUpdate(info.Fullpath, info, (e, v) => { return info; });
}
}
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void OnFileChange(object source, FileSystemEventArgs e)
{
InputFileInfo info;
if (this.inputFileList.TryGetValue(e.FullPath, out info))
{
DateTime lastWriteTime = System.IO.File.GetLastWriteTime(e.FullPath);
if (info.LastHandledChange != lastWriteTime)
{
TimeSpan span = lastWriteTime.Subtract(info.LastHandledChange);
if (span.Days == 0 && span.Hours == 0 && span.Minutes == 0 && span.Seconds == 0 && span.TotalMilliseconds < this.MinimumFileChangePeriod)
{
// Event ignored due to required minimum file change period;
}
else
{
info.LastHandledChange = lastWriteTime;
this.inputFileList.AddOrUpdate(e.FullPath, info, (a, v) => { return info; });
lock (this.readLockerObject)
{
this.ReadFile(e.FullPath);
}
}
}
}
}
private bool ReadFile(string filepath, int count, bool directReading)
{
StreamReader sr = this.OpenStreamReader(file);
if (sr != null)
{
string line;
string[] split;
int signalId;
double value;
while ((line = sr.ReadLine()) != null)
{
// do sth. with line
}
}
}
}
internal class InputFileInfo : IDisposable
{
public string Dir { get; private set; }
public string File { get; private set; }
public FileSystemWatcher Watcher { get; private set; }
public DateTime LastHandledChange { get; set; }
private string fullpath;
public string Fullpath
{
get
{
return this.fullpath;
}
set
{
this.fullpath = BaseHelper.GetFullFilePath(value);
this.Dir = Directory.GetParent(this.fullpath).ToString();
this.File = this.fullpath.Replace(this.Dir + "\\", string.Empty);
this.Watcher = new FileSystemWatcher();
this.Watcher.Path = this.Dir;
this.Watcher.Filter = this.File;
this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.Watcher.EnableRaisingEvents = true;
}
}
public void Dispose()
{
if (this.Watcher != null)
{
this.Watcher.Dispose();
this.Watcher = null;
}
}
}
You have very complex code. My code for watcher is about 10 lines.
There is no need to offload initialization of watcher to another thread. Initialize it on main thread. The watcher watches files in background thread and fires the event when change occurs so there is no need to call WaitForChanged() method.
I've checked my working code and compared it with yours and the only two differences are:
1. My filters are: NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName
2. I don't call WaitForChanged() method
3. I have [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] applied to all methods which interact with watcher.
Furthermore, make sure the file you are monitoring is not in one of the system directories... The watcher does not work well with those files. I banged my head up the wall for half a day long because I was trying to watch file on C:.
I am getting the token from acs as describe in this post Validating a SWT Token REST WCF Service
But i was not able to extract the ACS token.
Could you please help me in that.
Actually the class the extract the acs token "JsonNotifyRequestSecurityTokenResponse.FromJson"
I was not able to get that as the class as the link provided is not working.
I think you posted this question as an answer at my question some hours ago and then deleted it. So i looked up my old project and found the code you need:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Tokens;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web;
using System.Xml.Linq;
using Thinktecture.IdentityModel.Extensions;
namespace Security {
[DataContract]
public class JsonNotifyRequestSecurityTokenResponse {
[DataMember(Name = "appliesTo", Order = 1)]
public string AppliesTo { get; set; }
[DataMember(Name = "context", Order = 2)]
public string Context { get; set; }
[DataMember(Name = "created", Order = 3)]
public string Created { get; set; }
[DataMember(Name = "expires", Order = 4)]
public string Expires { get; set; }
[DataMember(Name = "securityToken", Order = 5)]
public string SecurityTokenString { get; set; }
[DataMember(Name = "tokenType", Order = 6)]
public string TokenType { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
public GenericXmlSecurityToken SecurityToken { get; set; }
public static JsonNotifyRequestSecurityTokenResponse FromJson(string jsonString) {
JsonNotifyRequestSecurityTokenResponse rstr;
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString));
var serializer = new DataContractJsonSerializer(typeof(JsonNotifyRequestSecurityTokenResponse));
rstr = serializer.ReadObject(memoryStream) as JsonNotifyRequestSecurityTokenResponse;
memoryStream.Close();
ParseValues(rstr);
return rstr;
}
private static void ParseValues(JsonNotifyRequestSecurityTokenResponse rstr) {
rstr.ValidFrom = ulong.Parse(rstr.Created).ToDateTimeFromEpoch();
rstr.ValidTo = ulong.Parse(rstr.Expires).ToDateTimeFromEpoch();
rstr.SecurityTokenString = HttpUtility.HtmlDecode(rstr.SecurityTokenString);
var xml = XElement.Parse(rstr.SecurityTokenString);
string idAttribute = "";
string tokenType = "";
switch (rstr.TokenType) {
case SecurityTokenTypes.Saml11:
idAttribute = "AssertionID";
tokenType = SecurityTokenTypes.Saml11;
break;
case SecurityTokenTypes.Saml2:
idAttribute = "ID";
tokenType = SecurityTokenTypes.Saml2;
break;
case SecurityTokenTypes.SWT:
idAttribute = "Id";
tokenType = SecurityTokenTypes.SWT;
break;
}
if (tokenType == SecurityTokenTypes.Saml11 || tokenType == SecurityTokenTypes.Saml2) {
var tokenId = xml.Attribute(idAttribute);
var xmlElement = xml.ToXmlElement();
SecurityKeyIdentifierClause clause = null;
if (tokenId != null) {
clause = new SamlAssertionKeyIdentifierClause(tokenId.Value);
}
rstr.SecurityToken = new GenericXmlSecurityToken(
xmlElement,
null,
rstr.ValidFrom,
rstr.ValidTo,
clause,
clause,
new ReadOnlyCollection<IAuthorizationPolicy>(new List<IAuthorizationPolicy>()));
}
}
private class SecurityTokenTypes {
public const string Saml2 = "urn:oasis:names:tc:SAML:2.0:assertion";
public const string Saml11 = "urn:oasis:names:tc:SAML:1.0:assertion";
public const string SWT = "http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0";
}
}
}
You should get the Thinktecture.IdentityModel.Extensions assembly from: http://identitymodel.codeplex.com/
I hope this helps.
EDIT - SWT token handling
I don't know if it helps, but I've found a SWTModule which should handle a SWT token.
public class SWTModule : IHttpModule {
void IHttpModule.Dispose() {
}
void IHttpModule.Init(HttpApplication context) {
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e) {
//HANDLE SWT TOKEN VALIDATION
// get the authorization header
string headerValue = HttpContext.Current.Request.Headers.Get("Authorization");
// check that a value is there
if (string.IsNullOrEmpty(headerValue)) {
throw new ApplicationException("unauthorized");
}
// check that it starts with 'WRAP'
if (!headerValue.StartsWith("WRAP ")) {
throw new ApplicationException("unauthorized");
}
string[] nameValuePair = headerValue.Substring("WRAP ".Length).Split(new char[] { '=' }, 2);
if (nameValuePair.Length != 2 ||
nameValuePair[0] != "access_token" ||
!nameValuePair[1].StartsWith("\"") ||
!nameValuePair[1].EndsWith("\"")) {
throw new ApplicationException("unauthorized");
}
// trim off the leading and trailing double-quotes
string token = nameValuePair[1].Substring(1, nameValuePair[1].Length - 2);
// create a token validator
TokenValidator validator = new TokenValidator(
this.acsHostName,
this.serviceNamespace,
this.trustedAudience,
this.trustedTokenPolicyKey);
// validate the token
if (!validator.Validate(token)) {
throw new ApplicationException("unauthorized");
}
}
}
And you also need the TokenValidator:
public class TokenValidator {
private string issuerLabel = "Issuer";
private string expiresLabel = "ExpiresOn";
private string audienceLabel = "Audience";
private string hmacSHA256Label = "HMACSHA256";
private string acsHostName;
private string trustedSigningKey;
private string trustedTokenIssuer;
private string trustedAudienceValue;
public TokenValidator(string acsHostName, string serviceNamespace, string trustedAudienceValue, string trustedSigningKey) {
this.trustedSigningKey = trustedSigningKey;
this.trustedTokenIssuer = String.Format("https://{0}.{1}/",
serviceNamespace.ToLowerInvariant(),
acsHostName.ToLowerInvariant());
this.trustedAudienceValue = trustedAudienceValue;
}
public bool Validate(string token) {
if (!this.IsHMACValid(token, Convert.FromBase64String(this.trustedSigningKey))) {
return false;
}
if (this.IsExpired(token)) {
return false;
}
if (!this.IsIssuerTrusted(token)) {
return false;
}
if (!this.IsAudienceTrusted(token)) {
return false;
}
return true;
}
public Dictionary<string, string> GetNameValues(string token) {
if (string.IsNullOrEmpty(token)) {
throw new ArgumentException();
}
return
token
.Split('&')
.Aggregate(
new Dictionary<string, string>(),
(dict, rawNameValue) => {
if (rawNameValue == string.Empty) {
return dict;
}
string[] nameValue = rawNameValue.Split('=');
if (nameValue.Length != 2) {
throw new ArgumentException("Invalid formEncodedstring - contains a name/value pair missing an = character");
}
if (dict.ContainsKey(nameValue[0]) == true) {
throw new ArgumentException("Repeated name/value pair in form");
}
dict.Add(HttpUtility.UrlDecode(nameValue[0]), HttpUtility.UrlDecode(nameValue[1]));
return dict;
});
}
private static ulong GenerateTimeStamp() {
// Default implementation of epoch time
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToUInt64(ts.TotalSeconds);
}
private bool IsAudienceTrusted(string token) {
Dictionary<string, string> tokenValues = this.GetNameValues(token);
string audienceValue;
tokenValues.TryGetValue(this.audienceLabel, out audienceValue);
if (!string.IsNullOrEmpty(audienceValue)) {
if (audienceValue.Equals(this.trustedAudienceValue, StringComparison.Ordinal)) {
return true;
}
}
return false;
}
private bool IsIssuerTrusted(string token) {
Dictionary<string, string> tokenValues = this.GetNameValues(token);
string issuerName;
tokenValues.TryGetValue(this.issuerLabel, out issuerName);
if (!string.IsNullOrEmpty(issuerName)) {
if (issuerName.Equals(this.trustedTokenIssuer)) {
return true;
}
}
return false;
}
private bool IsHMACValid(string swt, byte[] sha256HMACKey) {
string[] swtWithSignature = swt.Split(new string[] { "&" + this.hmacSHA256Label + "=" }, StringSplitOptions.None);
if ((swtWithSignature == null) || (swtWithSignature.Length != 2)) {
return false;
}
HMACSHA256 hmac = new HMACSHA256(sha256HMACKey);
byte[] locallyGeneratedSignatureInBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(swtWithSignature[0]));
string locallyGeneratedSignature = HttpUtility.UrlEncode(Convert.ToBase64String(locallyGeneratedSignatureInBytes));
return locallyGeneratedSignature == swtWithSignature[1];
}
private bool IsExpired(string swt) {
try {
Dictionary<string, string> nameValues = this.GetNameValues(swt);
string expiresOnValue = nameValues[this.expiresLabel];
ulong expiresOn = Convert.ToUInt64(expiresOnValue);
ulong currentTime = Convert.ToUInt64(GenerateTimeStamp());
if (currentTime > expiresOn) {
return true;
}
return false;
} catch (KeyNotFoundException) {
throw new ArgumentException();
}
}
}