I wrote a non-static Generic class instantiator for my Abstract Factory design, and use Singleton approach to make sure that only 1 instance of the instantiator will be initialized for every client request.
public sealed class FactoryInstantiator<T> where T: class
{
private static readonly FactoryInstantiator<T> _instance = new Instantiator<T>();
public static FactoryInstantiator<T> Instance
{
get
{
_client = HttpContext.Current.Session["ClientCode"].ToString();
return _instance;
}
}
private static string _client;
private string _className;
private string _fullyQualifiedClassName;
private string _assemblyName;
private FactoryInstantiator() { }
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
return (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
}
}
I abstracted the the whole namespace for each Client
namespace InventorySuite.Factory.BusinessLogic
{
// abstract factory
public abstract class InvoiceFactory
{
public abstract void Set() { }
}
}
namespace InventorySuite.Client1.BusinessLogic
{
// concrete invoice class for Client1
public class Invoice : InvoiceFactory
{
public override void Set() { }
}
}
namespace InventorySuite.Client2.BusinessLogic
{
// concrete invoice class for Client2
public class Invoice : InvoiceFactory
{
public override void Set() { }
}
}
protected void Page_Load(object sender, EventArgs e)
{
InvoiceFactory clientInvoice;
Session.Add("ClientCode", "Client1");
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
Session["ClientCode"] = "Client2";
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
}
It works well and already tested it, but my question is about its efficiency/performance hit, since I use reflection here, and for the Singleton approach if it has multi-threading issues (afaik, the singleton instance will be shared in all clients). I will also appreciate any other approach on this. thanks
You will not have any multi-threading issue since you're creating a new instance every time.
About the performance. You can measure the time creating 100 instances:
long ini = Environment.TickCount;
for (int i = 0; i < 100; i++)
{
Session["ClientCode"] = "Client2";
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
}
long timeCreate100Instances = Environment.TickCount - ini;
Using reflection, the performance hit resides in loading the assembly. I think that in your case, the class you're loading is in the same dll, you you also will not experiment any performance issue.
In other case, you can cache the Assembly obejcts in a Hastable/Dictionary in your CreateInstance() method.
using Richard and Daniel suggestions, I was able to reduce the performance hit of reflection using Caching. I therefore conclude that Reflection really has huge performance issues.
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
// use caching
T obj;
if (HttpContext.Current.Cache[_fullyQualifiedClassName] == null)
{
obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
HttpContext.Current.Cache.Insert(_fullyQualifiedClassName, obj, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
}
else
{
obj = (T)HttpContext.Current.Cache[_fullyQualifiedClassName];
}
return obj;
}
protected void Page_Load(object sender, EventArgs e)
{
InvoiceFactory inv;
Stopwatch globalTimer = Stopwatch.StartNew();
//normal instantiation
globalTimer = Stopwatch.StartNew();
for (int x = 0; x <= 10000; x++)
inv = new InventorySuit.Client1.BusinessLogic.Invoice;
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 0ms
// using singleton factory w/o caching
globalTimer = Stopwatch.StartNew();
for (int x = 0; x <= 10000; x++)
inv = new FactoryInstantiator<InvoiceFactory>().CreateInstance();
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 129ms
// using singleton factory w/ caching
for (int x = 0; x <= 10000; x++)
inv = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 21ms
}
Loading the assemblies in Session State to solve multi-threading issue.
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
T obj;
var assemblies = HttpContext.Current.Session["ASSEMBLIES"] as Dictionary<string, T>;
if (assemblies == null)
{
assemblies = new Dictionary<string, T>();
assemblies.Add(_fullyQualifiedClassName, null);
HttpContext.Current.Session.Add("ASSEMBLIES", assemblies);
}
obj = assemblies[_fullyQualifiedClassName] as T;
if (obj == null)
{
obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
assemblies[_fullyQualifiedClassName] = obj;
}
return obj;
}
Related
Is there a way to get rid of practise.AcademicProgram in the output when the display() is called for an object initialized by the parameterized constructor. In my code below this occurs when s2.Display(); is executed.
Below is my working code for the same:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace practise
{
public class AcademicProgram
{
//Memnber variables
private int programCode;
private string programName;
private int programCredits;
//Properties for reading and writting the values of private fields
public int ProgramCode { get => programCode; set => programCode = value; }
public string ProgramName { get => programName; set => programName = value; }
public int ProgramCredits { get => programCredits; set => programCredits = value; }
//Default Constructor
public AcademicProgram()
{
}
//Parameterized Constructor
public AcademicProgram (int programCode, string programName, int programCredits)
{
this.programCode = programCode;
this.programName = programName;
this.programCredits = programCredits;
}
public void DisplayResults()
{
Console.WriteLine("Program Code: {0}\nProgram Name: {1}\nProgram Credits: {2}\n", programCode, programName, programCredits);
Console.WriteLine();
}
}
public class Instructor
{
//Member variables
public string forename;
public string surname;
public int identificationNumber;
public AcademicProgram academicProgram;
//Default Constructor
public Instructor()
{
this.forename = string.Empty;
this.surname = string.Empty;
this.identificationNumber = 0;
this.academicProgram = null;
}
//Parameterized Constructor
public Instructor(string forename, string surname, int identificationNumber, AcademicProgram academicProgram)
{
this.forename = forename;
this.surname = surname;
this.identificationNumber = identificationNumber;
this.academicProgram = academicProgram;
}
// Member Function to display values of member variables on Console.
public void Display()
{
Console.WriteLine(this.forename + ", " + this.surname + ", " + this.identificationNumber + ", " + this.academicProgram);
Console.WriteLine();
}
//Driver function
static void Main(string[] args)
{
//Instantiating object to call non-static member method
Instructor p = new Instructor();
p.Go();
Console.ReadKey();
}
//Non-static Member function
public void Go()
{
//Instantiating object without passing any values on runtime.
Instructor s = new Instructor();
//Instantiating object of AcademicProgram class without passing any values on runtime.
AcademicProgram progName = new AcademicProgram ();
//Set the values of fields using properties
progName.ProgramCode = 8230;
progName.ProgramName = "Systems Development: Cocnepts and Analysis";
progName.ProgramCredits = 4;
// Instantiating object while providing values on runtime.
Instructor s2 = new Instructor("Eddie ", "Jessup", 2394589, progName);
//Call to display method
s.Display();
s2.Display();
progName.DisplayResults();
}
}
}
Which gives an output shown here
Aside from override ToString(), I was able to fix it by a simple fix in display() of Instructor class as below:
public void Display()
{
Console.WriteLine(this.forename + ", " + this.surname + ", " + this.identificationNumber + ", " + this.academicProgram.ProgramCode + ", " + this.academicProgram.ProgramName + ", " + this.academicProgram.ProgramCredits);
Console.WriteLine();
}
I am new to MemoryMappedFiles. I am creating a file using MemoryMappedFile and I want to write a json string to it. But the problem is for the Write method of the MemoryMappedViewAccessor none of the overloads takes in a string. Please help.
public class ApplicationSettingsViewModel
{
ApplicationSettingsModel model;
MemoryMappedFile mmf = null;
//This is not a singleton class but I guess it has to be one but its ok for demonstration.
public ApplicationSettingsViewModel()
{
model = new ApplicationSettingsModel();
CreateFileUsingMemoryMap();
}
private void CreateFileUsingMemoryMap()
{
var info = Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "/" + model.Data.Settings.OrcaUISpecificSettings.TimeOutFolder);
string path = Path.Combine(info.FullName + "/" + model.Data.Settings.OrcaUISpecificSettings.File);
mmf = MemoryMappedFile.CreateFromFile(path, FileMode.CreateNew, "MyMemoryFile", 1024 * 1024, MemoryMappedFileAccess.ReadWrite);
}
public MemoryMappedViewAccessor GetAccessor()
{
MemoryMappedViewAccessor mmvAccessor = null;
if (mmf != null)
{
mmvAccessor = mmf.CreateViewAccessor();
}
return mmvAccessor;
}
}
public partial class MainWindow: Window
{
private readonly DispatcherTimer _activityTimer;
private Point _inactiveMousePosition = new Point(0, 0);
private ApplicationSettingsViewModel AppViewModel;
Mutex mutex = new Mutex(false, "OrcaGeneStudyMutex");
public MainWindow()
{
InitializeComponent();
}
public void WriteToFile(string status)
{
Root root = new Root();
root.AllApplications.Add(new DataToWrite()
{
AppName = "DevOrca", Status = status
});
var jsonString = JsonConvert.SerializeObject(root);
var Accessor = AppViewModel.GetAccessor();
mutex.WaitOne();
//Serialize Contents
Accessor.Write(1024, jsonString); //it gives a complilation error when i try to pass the json string,
}
}
I have this very basic class library that has inherited from System.Attribute. I also signed it as an assembly so the dll can be used in another program.
namespace BearData
{
public class BearData : Attribute
{
private string[] array1;
private string bear = "Bear";
private int weight;
public BearData(string bear)
{
this.bear = bear;
}
public string Bear
{
get
{
return bear;
}
set
{
bear = value;
}
}
public int Weight
{
get
{
return weight;
}
set
{
weight = value;
}
}
public string[] BearTypes()
{
array1 = new string[8];
array1[0] = "Brown/Grizzly";
array1[1] = "Polar";
array1[2] = "Asian Black";
array1[3] = "American Black";
array1[4] = "Sun";
array1[5] = "Sloth";
array1[6] = "Spectacled";
array1[7] = "Giant Panda";
return array1;
}
}
}
Here it is used in a basic console application. However due to my professor's cryptic, vague, and enigmatic nature, i'm at a stand still on getting this to work.I get an error from this line:
bearAttribute = (BearData.BearData)attrs[0];
"An unhandled exception of type 'System.IndexOutOfRangeException'
occurred in Assigntment5_Console.exe" is the exact error.
I guess my specific question what is causing that error?
But also more generally, Is this a good/proper way to use attributes when theyre from an outside library? It seems strange to me that there's random arrays thrown in here and that i'm casting an array to the attribute class?
By the way. This is how my professor wrote the code for an attribute class isolated in a single Visual Studio instance. He also had an example of class library dll exporting and I was left to my own device to figure out how to combine the 2.
using BearData;
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MemberInfo attributeInfo;
attributeInfo = typeof(BearData.BearData);
object[] attrs = attributeInfo.GetCustomAttributes(false);
//for (int i = 0; i < attrs.Length; i++)
//{
// Console.WriteLine(attrs[i]);
//}
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}
You have defined the BearData attribute on the Program.Main() method, so you should be looking for the attribute there
The following code should fix your problem
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MethodBase method = MethodBase.GetCurrentMethod();
object[] attrs = method.GetCustomAttributes(typeof(BearData.BearData), true);
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}
I'm not much familiar with WinRT. I'm encountering an unexpected behavior. I've a static variable _Verses that is initialized in static constructor of class. So expected behavior is _Verses will be initialized before first reference to static method as explained in When is a static constructor called in C#?
But when I call a static async function LoadData (WinRT) I got exception.
Object Reference not set to an instance of object.
My Code is:
public VerseCollection
{
public const int TotalVerses = 6236;
static Verse[] _Verses;
static VerseCollection()
{
_Verses = new Verse[TotalVerses];
}
internal static async void LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public Book
{
public static async Task<Book> CreateInstance()
{
VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
}
}
I call the function CreateInstance as:
async void DoInit()
{
await DigitalQuran.Book.CreateInstance();
}
Same code is working in desktop but not working for WinRT. Full Code of Book Class for Desktop is here and for VerseCollection class is here
EDIT:
Complete code is here
public class Book : VerseSpan
{
public static async Task<Book> CreateInstance()
{
_Instance = new Book();
VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
PrivateStorage.LoadQuranObjectsFromMetadata();
// Some Other Operations too
return _Instance;
}
}
public class VerseCollection
{
static Verse[] _Verses = new Verse[TotalVerses];
internal static async void LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public class Verse
{
public Verse(int number, int firstWordIndex, string[] words)
{
GlobalNumber = number + 1;
Words = new WordCollection(firstWordIndex, words, this);
}
}
public class WordCollection : ReadOnlyCollection<Word>
{
public const int TotalWords = 77878;
static Word[] _Words = new Word[TotalWords];
static string[] _WordsText = new string[TotalWords];
public WordCollection(int startIndex, int count)
: base(count)
{
this.startIndex = startIndex;
}
internal WordCollection(int startId, string[] words, Verse verse) : this(startId, words.Length)
{
int max = words.Length + startId;
for (int i = startId; i < max; i++)
{
_Words[i] = new Word(i, verse);
_WordsText[i] = words[i - startId];
}
}
}
public abstract class ReadOnlyCollection<T> : IEnumerable<T>
{
public ReadOnlyCollection(int count)
{
Count = count;
}
}
public class PrivateStorage
{
internal static async void LoadQuranObjectsFromMetadata()
{
using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
{
/* 1 */ ChapterCollection.LoadData(EnumerateChapters(reader));
/* 2 */ PartCollection.LoadData(EnumerateParts(reader));
/* Some other tasks */
}
}
static IEnumerator<ChapterMeta> EnumerateChapters(BinaryReader reader)
{
for (int i = 0; i < ChapterCollection.TotalChapters; i++)
{
yield return new ChapterMeta()
{
StartVerse = reader.ReadInt32(),
VerseCount = reader.ReadInt32(),
BowingCount = reader.ReadInt32(),
Name = reader.ReadString(),
EnglishName = reader.ReadString(),
TransliteratedName = reader.ReadString(),
RevelationPlace = (RevelationPlace)reader.ReadByte(),
RevelationOrder = reader.ReadInt32()
};
}
}
static IEnumerator<PartMeta> EnumerateParts(BinaryReader reader)
{
for (int i = 0; i < PartCollection.TotalParts; i++)
{
yield return new PartMeta()
{
StartVerse = reader.ReadInt32(),
VerseCount = reader.ReadInt32(),
ArabicName = reader.ReadString(),
TransliteratedName = reader.ReadString()
};
}
}
}
public class ChapterCollection : ReadOnlyCollection<Chapter>
{
public const int TotalChapters = 114;
static Chapter[] _Chapters = new Chapter[TotalChapters];
internal static void LoadData(IEnumerator<ChapterMeta> e)
{
for (int i = 0; i < TotalChapters; i++)
{
e.MoveNext();
_Chapters[i] = new Chapter(i, e.Current);
}
}
}
public class PartCollection : ReadOnlyCollection<Part>
{
public const int TotalParts = 30;
static Part[] _Parts = new Part[TotalParts];
internal static void LoadData(IEnumerator<PartMeta> e)
{
for (int i = 0; i < TotalParts; i++)
{
e.MoveNext();
_Parts[i] = new Part(i, e.Current);
}
}
}
When I run the code with debugger no exception is raised. Further After exception visual studio shows some times in class VerseCollection in function LoadData on line _Verses[i] = new Verse(i, wId, retValue); (_Verses is null) and some times in class ChapterCollection in Function LoadData on line _Chapters[i] = new Chapter(i, e.Current); (_Chapters is null)
There was issue with asynchronous call. File reading is asynchronous operation in WinRT. As We can't call async method with void return type with await statement. So next instructions executes without waiting for completion of last executing as another Task. This leads to NullReferanceExecption.
I managed to solve my problems by changing return type of all async operations from void to Task and called them with await like in the code below.
public class Book : VerseSpan
{
public static async Task<Book> CreateInstance()
{
_Instance = new Book();
await VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
await PrivateStorage.LoadQuranObjectsFromMetadata();
// Some Other Operations too
return _Instance;
}
}
public class VerseCollection
{
static Verse[] _Verses = new Verse[TotalVerses];
internal static async Task LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public class PrivateStorage
{
internal static async Task LoadQuranObjectsFromMetadata()
{
using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
{
/* Some tasks */
}
}
}
Because it is running on Desktop but not WinRT, it leads me to believe there is an issue with your asynchronous call. Because you are doing this asynchronously, there is no gaurantee that the constructor (static or not) will be finished running before the call to LoadData. Make sure that your constructor has finished executing before calling LoadData function, and this should give you consistent behaviour.
Easiest way that I can add a title row to each of these files. I can't put it in the Log()'s because it would add the title row each time I reference the test.Log()'s
I thought about referencing 3 new int fields and each time I reference a Log1/2/3() it checks the value of the int and if it's 1 then add the title column else don't.I did one below but it seems redundant I figured there must be a better way.
public class Test
{
public static string File1 = DateTime.Now.ToString("yyyy-MM-dd") + "test1.csv";
public static int a = 0;
public static string File2 = DateTime.Now.ToString("yyyy-MM-dd") + "test2.csv";
public static string File3 = DateTime.Now.ToString("yyyy-MM-dd") + "test3.csv";
private static object _lockObj = new Object();
public static void Log1(string error, string record)
{ a++;
if(a==1){
lock (_lockObj)
{
File.AppendAllText(test.File1, "ERROR" + Environment.NewLine)
File.AppendAllText(test.File1, error + "," + record + Environment.NewLine);
}
}
else{
File.AppendAllText(test.File1, error + "," + record + Environment.NewLine);
}
}
public static void Log2(string message)
{
lock (_lockObj)
{
File.AppendAllText(test.File2, message + Environment.NewLine);
}
}
public static void Log3(Test c)
{
lock (_lockObj)
{
File.AppendAllText(test.File3, c + Environment.NewLine);
}
}
}
You may create a class like this:-
public class HeaderOnceAppender : RollingFileAppender
{
protected override void WriteHeader()
{
if (LockingModel.AcquireLock().Length == 0)
{
base.WriteHeader();
}
}
If you want put a Title row at the top of the file when creating it...
if(!System.IO.File.Exists(test.File1)) //if the file doesn't exist
{ //insert your header at the top
File.AppendAllText(test.File1,"My Header Line")
}
This will only append once when the file is first created