How can I write a using block in another way? - c#

I have a class with different methods from a Windows Form. In my test code, I used this to create a new OpenXML Document:
using (WordprocessingDocument package = WordprocessingDocument.Create(docName, WordprocessingDocumentType.Document))
But this seems not working on multiple methods. How can I fix it? It won't work without the using, so after a bit of research I found out that this class was IDisposable.
But I have 2 needs right now:
1) If the file exists, the document has to open instead of creating a new one.
2) The docName, which contains the path to the file that he's going to save, has to be reachable end used in the block as seen above.
Is there a way to do this?
This is my code right now:
using System;
using System.IO;
using System.Windows.Forms;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace WordExample
{
public partial class Word : Form
{
private string _docName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + #"\xxx2.docx";
private WordprocessingDocument _package;
private Document _doc;
private Body _body;
public Word()
{
InitializeComponent();
/*if (File.Exists(_docName))
{
_package = WordprocessingDocument.Open(_docName, false);
_doc = _package.MainDocumentPart.Document;
_body = _doc.Body;
}
else
{
_package = WordprocessingDocument.Create(_docName, WordprocessingDocumentType.Document);
_package.AddMainDocumentPart();
_doc = _package.MainDocumentPart.Document;
_body = new Body();
}*/
_package = WordprocessingDocument.Create(_docName, WordprocessingDocumentType.Document);
_package.AddMainDocumentPart();
_doc = new Document();
_body = new Body();
}
private void Word_Load(object sender, EventArgs e)
{
}
private void btnAddParagraph_Click(object sender, EventArgs e)
{
/*Paragraph p = new Paragraph();
Text t = new Text(txtTekstParagraaf.Text);
Run r = new Run();
RunProperties rPr = new RunProperties();
if (chkBold.Checked)
{
Bold b = new Bold();
rPr.Append(b);
}
if (chkItalic.Checked)
{
Italic b = new Italic();
rPr.Append(b);
}
if (chkUnderline.Checked)
{
Underline b = new Underline();
rPr.Append(b);
}
//RunProperties
//r.PrependChild<RunProperties>(rPr);
r.PrependChild(rPr);
r.AppendChild(t);
p.AppendChild(r);
_body.AppendChild(p);*/
Save();
}
private void Save()
{
_doc.AppendChild(_body);
_package.MainDocumentPart.Document = _doc;
// Save changes to the main document part.
_package.MainDocumentPart.Document.Save();
}
}
}
But this code generates an error when trying to open the created document afterwards AND when I try to open an document instead of creating one.

You can try using trenary operator here:
using (WordprocessingDocument package = File.Exists(docName) ?
WordprocessingDocument.Create(docName, WordprocessingDocumentType.Document) :
WordprocessingDocument.Open(docName, WordprocessingDocumentType.Document)) {
...
}

If the file exists, the document has to open instead of creating a new one.
So why don't you use an if statement?
if(File.Exists(docName))
{
using(var package = ..) // open file
{
...
}
}
else
{
using(var package = ..) // create file
{
...
}
}

You are not forced to instantiate variable directly in using statement. You can move creation to separate method:
private WordprocessingDocument GetPackage(string docName)
{
var docType = WordprocessingDocumentType.Document;
if (File.Exists(docName))
return WordprocessingDocument.Open(docName, docType);
return WordprocessingDocument.Create(docName, docType);
}
Now your code will look like:
using(var package = GetPackage(docName))
{
// ...
}
UPDATE: If you want to reuse disposable dependency in all methods of class (as you stated in comments) you should implement IDisposale by class which holds disposable dependency, and dispose that dependency when you are disposing class:
public class Foo : IDisposable
{
private readonly WordprocessingDocument _package;
public Foo()
{
_package = GetPackage(docName); // implemented as above
}
public void Method1()
{
// use _package without `using` block
}
public void Method2()
{
// use _package without `using` block
}
public void Dispose()
{
if (_package != null)
_package.Dispose();
}
}
Then wrap instance of Foo into using block:
using(var foo = new Foo()) // _package created here
{
foo.Method1(); // same _package instance used by both methods
foo.Method2();
} // _package will be disposed here

Related

How to save raw data as text file/json?

So I'm only a week into learning C# and I've been working on this one problem for 8 hours. I'll describe what I'm trying to do, and if there's a better way I would really appreciate any guidance.
In the code below, I'm storing info from a Firebase database called snapshot, and trying to save it as a text file data.json. Later in the code, it reads data.json and calls allRoundData from the file.
Right now in Unity the application doesn't compile and says "Object reference not set to an instance of an object" for allRoundData = loadedData.allRoundData;
I also checked and nothing is writing to the text file when I open it in explorer.
I'm pulling trivia questions and answers from a Firebase database, so however I do this it's important that the user can't access the answers. Would it be more secure to skip writing it to a text file and just declare allRoundData = snapshot.allRoundData? The problem is that allRoundData is a string array and the snapshot is an object. Is there a way to easily convert it or how can I solve this in the simplest way?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
using Firebase;
using Firebase.Unity.Editor;
using Firebase.Database;
public class DataController : MonoBehaviour
{
public class FirebaseStart: MonoBehaviour
{
void Start()
{
// Set up the Editor before calling into the realtime database.
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("https://FIREBASEDATABASE");
// Get the root reference location of the database.
DatabaseReference reference =
FirebaseDatabase.DefaultInstance.RootReference;
FirebaseDatabase.DefaultInstance
.GetReference("allRoundData")
.GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
string gameDataFileName = "data.json";
string filePath =
Path.Combine(Application.streamingAssetsPath, gameDataFileName);
DataSnapshot snapshot = task.Result;
// Do something with snapshot...
string rawData = snapshot.GetRawJsonValue();
StreamWriter sw = new StreamWriter(filePath);
sw.WriteLine(rawData);
sw.Close();
}
});
}
}
private RoundData[] allRoundData;
private PlayerProgress playerProgress;
// Use this for initialization
void Start ()
{
DontDestroyOnLoad(gameObject);
LoadGameData();
LoadPlayerProgress();
SceneManager.LoadScene("MenuScreen");
}
public RoundData GetCurrentRoundData()
{
return allRoundData [0];
}
public void SubmitNewPlayerScore(int newScore)
{
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
public int GetHighestPlayerScore()
{
return playerProgress.highestScore;
}
// Update is called once per frame
void Update () {
}
private void LoadPlayerProgress()
{
playerProgress = new PlayerProgress();
if (PlayerPrefs.HasKey("highestScore"))
{
playerProgress.highestScore = PlayerPrefs.GetInt("highestScore");
}
}
private void SavePlayerProgress()
{
PlayerPrefs.SetInt("highestScore", playerProgress.highestScore);
}
private void LoadGameData()
{
string gameDataFileName = "data.json";
string filePath = Path.Combine(Application.streamingAssetsPath, gameDataFileName);
if (File.Exists(filePath))
{
string dataAsJson = File.ReadAllText(filePath);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
}
else
{
Debug.LogError("Cannot load game data!");
}
}
}

XmlSerializer.Serialize() fails to update XML file

So far I've followed the textbook route of adding adding data to an XML file; first, I created a class:
[Serializable]
public class Note
{
public string Notes { get; set; }
}
And then done this:
private void addButton_Click(object sender, RoutedEventArgs e)
{
string stringToAdd = textBox.Text;
Notes.Add(stringToAdd);
using (StringWriter myStringWirter = new StringWriter())
{
XmlSerializer myXML = new XmlSerializer(typeof(Note));
Note myNote = new Note();
myNote.Notes = stringToAdd;
myXML.Serialize(myStringWirter, myNote);
using (StreamWriter myStreamWriter = new StreamWriter("Notes.xml"))
{
myStreamWriter.Write(myStringWirter);
}
}
But Notes.xml doesn't get updated. Why?
Edit. Now it works. Just by using List instead of Note.

Mono using statement bug with initialization

I think there is a bug in c# mono implementation.
using (ITransactional transaction = new TransactionalContext ())
using (ITransactional logTransaction =
new TransactionalContext(transaction.Connection ())){
...
}
Such code doesn't call constructor of TransactionalContext and immediatelly call Dispose() on transaction variable.
Could someone confirm that?
I'm compiling this code using Mono 3.12.1 (Windows)
Some investigation:
Such code work compiled by MS compiler and fail compiled by Mono
public class DispTest : IDisposable
{
private static int t = 0;
public DispTest()
{
Console.WriteLine("CONSTRUCTOR");
this.Text = "HELLO" + t.ToString();
t += 1;
}
public DispTest(string text)
{
this.Text = text;
Console.WriteLine(text);
}
public string Text { get; set; }
public void Dispose()
{
Console.WriteLine("DISPOSING");
}
}
//in main
using (DispTest t = new DispTest())
using(DispTest t2 = new DispTest(t.Text)){
}
using (DispTest t = new DispTest()){
using(DispTest t2 = new DispTest(t.Text)){
//this code will fail too.
}
}

OpenFileDialog filename serialization

In writing a program where I need to serialize an AppSettings object which consists of several properties including one that will be used to store a last used filename, I have found that the FileName property is placed into my object (by assignment) but it does not serialize to the xml file. No exceptions are thrown and no data is written.
But conversely, if I programmtically modify the object
tc.TheDataFile = "c:\\Documents And Settings\\SomeUser\\Sample\\a test file.txt";
instead of
tc.TheDataFile = theDialog.FileName;
That will work. Can someone please provide some insight with regard to what I am missing?
Here is a simple version of the program that is directly related to the problem.
The test class which will theoretically hold the AppSettings ---
[Serializable()]
public class TestClass
{
private string m_TheDataFile;
private bool m_UseLastKnownDataFile = true;
public bool UseLastKnownDataFile
{
get
{
return m_UseLastKnownDataFile;
}
set
{
m_UseLastKnownDataFile = value;
}
}
public string TheDataFile
{
get
{
return m_TheDataFile;
}
set
{
m_TheDataFile = value;
}
}
}
public class TestClassHelper
{
public static TestClass Load()
{
XmlSerializer serializer = new XmlSerializer(typeof(TestClass));
TestClass retVal;
TextReader reader = null;
bool fileNotFound = false; ;
try
{
reader = new StreamReader("TestClassConfig.xml");
}
catch (FileNotFoundException)
{
fileNotFound = true;
}
if (fileNotFound)
{
retVal = new TestClass();
}
else
{
retVal = (TestClass)serializer.Deserialize(reader);
reader.Close();
}
return retVal;
}
public static void Save(TestClass settings)
{
XmlSerializer serializer = new XmlSerializer(typeof(TestClass));
TextWriter writer = new StreamWriter("TestClassConfig.xml");
serializer.Serialize(writer, settings);
writer.Close();
}
}
And here is the form which will prompt the user for a filename. In this test, there is a form with one button.
public partial class Form1 : Form
{
TestClass tc = null;
public Form1()
{
InitializeComponent();
tc = TestClassHelper.Load();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog theDialog = new OpenFileDialog();
string fileName = string.Empty;
theDialog.CheckFileExists = true;
theDialog.CheckPathExists = true;
theDialog.Multiselect = false;
theDialog.FileName = string.Empty;
if (theDialog.ShowDialog() == DialogResult.OK)
{
tc.TheDataFile = theDialog.FileName;
}
else
{
tc.TheDataFile = string.Empty;
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
TestClassHelper.Save(tc);
}
}
Edit To Add:
I'm using Microsoft Visual Studio 2005 Team Edition w/Dot Net 2.0.50727 SP1, with no options to upgrade the development environment.
Solution
I'm not exactly sure why this happens, but the OpenFileDialog control must change the current operating directory of the program. When the object is deserialized to the xml file, it no longer writes where it originally opened. Rather it is created in the new directory.
I corrected the problem by making the XML read and write location more specific.
The problem is that you are setting tc.TheDataFile = fileName; after the if block, but you never assign anything to fileName except when you initialize it to string.Empty. One fix would be:
if (theDialog.ShowDialog() == DialogResult.OK)
{
fileName = theDialog.FileName;
}
// record last used data file
tc.TheDataFile = fileName;
or just
if (theDialog.ShowDialog() == DialogResult.OK)
{
tc.TheDataFile = theDialog.FileName;
}
Note that running your test in the debugger and "watch"ing the variables would have made the problem fairly easy to spot.

C# Access functions within methods

I've created a method in a class and I want to access the streamreader sr1 from another method within the same class but I can't seem to get access to the method!
public void showSelectedFile()
{
StreamReader sr1 = new StreamReader(File.OpenRead(ReturnTxt));
ReturnContenctRD = sr1.ReadToEnd();
}
public void DisposeSR1()
{
}
Can anyone explain to me how to access/alter these methods from other functions?
sr1 is local to the showSelectedFile() method; it goes out of scope when the method returns. If you want to make it visible to the other method, you have to make it a member of your class:
StreamReader sr1;
public void showSelectedFile()
{
sr1 = new StreamReader(File.OpenRead(ReturnTxt))
ReturnContenctRD = sr1.ReadToEnd();
}
public void DisposeSR1()
{
}
If it's just a matter of disposing it properly, wrap it in a using statement:
public void showSelectedFile()
{
using (StreamReader sr1 = new StreamReader(File.OpenRead(ReturnTxt)))
{
ReturnContenctRD = sr1.ReadToEnd();
}
}
You need to store the StreamReader in a class member variable
e.g.
class YourClass
{
private StreamReader _sr1;
public void showSelectedFile()
{
_sr1 = new StreamReader(File.OpenRead(ReturnTxt));
ReturnContenctRD = _sr1.ReadToEnd();
}
public void DisposeSR1()
{
if(_sr1 != null)
_sr1.Dispose()
}
}

Categories

Resources