I am trying to add a save method to a List that I can call and serialize the object to a file. I've got everything figured out except how to get the base class itself.
Here's my code:
/// <summary>
/// Inherits the List class and adds a save method that writes the list to a stream.
/// </summary>
/// <typeparam name="T"></typeparam>
class fileList<T> : List<T>
{
private static IFormatter serial = new BinaryFormatter();
private Stream dataStream;
/// <summary>
/// path of the data file.
/// </summary>
public string dataFile { get; set; }
/// <summary>
/// Sets the datafile path
/// </summary>
public fileList(string dataFile)
{
this.dataFile = dataFile;
}
/// <summary>
/// Saves the list to the filestream.
/// </summary>
public void Save()
{
dataStream = new FileStream(dataFile,
FileMode.Truncate, FileAccess.Write,
FileShare.Read);
//Right here is my problem. How do I access the base class instance.
serial.Serialize(dataStream, this.base);
dataStream.Flush();
dataStream.Close();
dataStream = null;
}
}
The line
serial.Serialize(dataStream, this.base);
should just be
serial.Serialize(dataStream, this);
Note however (thanks #Anders) that this will also serialize string dataFile. To avoid that, decorate that property with NonSerializedAttribute.
Having said that, I prefer to implement this type of functionality as a static method. With the advent of extension methods, I created a small extension class to handle this for any serializable type:
static public class SerialHelperExtensions
{
static public void Serialize<T>(this T obj, string path)
{
SerializationHelper.Serialize<T>(obj, path);
}
}
static public class SerializationHelper
{
static public void Serialize<T>(T obj, string path)
{
DataContractSerializer s = new DataContractSerializer(typeof(T));
using (FileStream fs = File.Open(path, FileMode.Create))
{
s.WriteObject(fs, obj);
}
}
static public T Deserialize<T>(string path)
{
DataContractSerializer s = new DataContractSerializer(typeof(T));
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
object s2 = s.ReadObject(fs);
return (T)s2;
}
}
}
You can certainly substitute BinaryFormatter for DataContractSerializer and use the same pattern.
Related
I have an application splittet into two projects: a web application and a class library. The Startup is only in the web application:
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
I wanna have my appsettings.json in that class library and being loaded from there. Is that possible? How can I do that?
The best solution I have found requires creating a new IFileProvider and IFileInfo, and then embedding the JSON settings files in your assembly.
The solution reuses the existing AddJsonFile logic. This way you only need to tell the configuration system how and where to locate the JSON file, not how to parse it.
The solution is compatible with .NET Core 1.0.
Usage:
public class Startup
{
private readonly AppSettings _appSettings;
public Startup(IHostingEnvironment env)
{
Assembly assembly = GetType().GetTypeInfo().Assembly;
new ConfigurationBuilder()
.AddEmbeddedJsonFile(assembly, "appsettings.json")
.AddEmbeddedJsonFile(assembly, $"appsettings.{env.EnvironmentName.ToLower()}.json")
.Build();
}
...
}
Embed the files by updating the project.json for the class library:
...
"buildOptions": {
"embed": [
"appsettings.json",
"appsettings.development.json",
"appsettings.production.json"
]
}
...
IEmbeddedFileInfo implementation:
public class EmbeddedFileInfo : IFileInfo
{
private readonly Stream _fileStream;
public EmbeddedFileInfo(string name, Stream fileStream)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (fileStream == null) throw new ArgumentNullException(nameof(fileStream));
_fileStream = fileStream;
Exists = true;
IsDirectory = false;
Length = fileStream.Length;
Name = name;
PhysicalPath = name;
LastModified = DateTimeOffset.Now;
}
public Stream CreateReadStream()
{
return _fileStream;
}
public bool Exists { get; }
public bool IsDirectory { get; }
public long Length { get; }
public string Name { get; }
public string PhysicalPath { get; }
public DateTimeOffset LastModified { get; }
}
IFileInfo implementation:
public class EmbeddedFileProvider : IFileProvider
{
private readonly Assembly _assembly;
public EmbeddedFileProvider(Assembly assembly)
{
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
_assembly = assembly;
}
public IFileInfo GetFileInfo(string subpath)
{
string fullFileName = $"{_assembly.GetName().Name}.{subpath}";
bool isFileEmbedded = _assembly.GetManifestResourceNames().Contains(fullFileName);
return isFileEmbedded
? new EmbeddedFileInfo(subpath, _assembly.GetManifestResourceStream(fullFileName))
: (IFileInfo) new NotFoundFileInfo(subpath);
}
public IDirectoryContents GetDirectoryContents(string subpath)
{
throw new NotImplementedException();
}
public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}
}
Create the easy to use AddEmbeddedJsonFile extension method.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddEmbeddedJsonFile(this IConfigurationBuilder cb,
Assembly assembly, string name, bool optional = false)
{
// reload on change is not supported, always pass in false
return cb.AddJsonFile(new EmbeddedFileProvider(assembly), name, optional, false);
}
}
Yes you could implement IConfigurationProvider
There is a base class ConfigurationProvider that you can inherit from then override all the virtual methods.
You can also see how the JsonConfigurationProvider is implemented.
So I guess your implementation could use the Json provider code internally against embedded json files.
Then you would also want to implement ConfigurationBuilder extension to register your provider similar as the code for using json config.
Someone else can correct me, but I don't think what you are looking for exists.
App Configs and AppSettings files are read at runtime by the application that is running.
The Class Library cannot see any AppSettings specific to itself, because when it runs at run time, it is in the folder of the running application.
The only potential way I can see for you to get your class library contain the json file, is to have the json file as an embedded resource.
Eg: In the solution, select the json file, and set it to Embedded Resource instead of 'content'.
The problem becomes getting the embedded config file out of your assembly, and then loaded.
AddJsonFile accepts a path to the json file.
You could however extract the Json file to a temp directory, then load from there.
static byte[] StreamToBytes(Stream input)
{
int capacity = input.CanSeek ? (int)input.Length : 0;
using (MemoryStream output = new MemoryStream(capacity))
{
int readLength;
byte[] buffer = new byte[capacity/*4096*/]; //An array of bytes
do
{
readLength = input.Read(buffer, 0, buffer.Length); //Read the memory data, into the buffer
output.Write(buffer, 0, readLength);
}
while (readLength != 0); //Do all this while the readLength is not 0
return output.ToArray(); //When finished, return the finished MemoryStream object as an array.
}
}
Assembly yourAssembly = Assembly.GetAssembly(typeof(MyTypeWithinAssembly));
using (Stream input = yourAssembly.GetManifestResourceStream("NameSpace.Resources.Config.json")) // Acquire the dll from local memory/resources.
{
byte[] byteData = StreamToBytes(input);
System.IO.File.WriteAllBytes(Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)+"//Config.json",new byte[]{});
}
var builder = new ConfigurationBuilder()
.AddJsonFile(Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)+"//Config.json");
You should in theory be able to specify a type from the class library, in order to help the c# code target that class library specifically. Then you just need to provide the namespace and path to the embedded json file.
Here is my solution, thanks Baaleos and Joe for your advices.
project.json
"resource": [
"appsettings.json"
]
startup.cs
var builder = new ConfigurationBuilder()
.Add(new SettingsConfigurationProvider("appsettings.json"))
.AddEnvironmentVariables();
this.Configuration = builder.Build();
namespace ClassLibrary
{
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
/// <summary>
/// A JSON file based <see cref="ConfigurationProvider"/> for embedded resources.
/// </summary>
public class SettingsConfigurationProvider : ConfigurationProvider
{
/// <summary>
/// Initializes a new instance of <see cref="SettingsConfigurationProvider"/>.
/// </summary>
/// <param name="name">Name of the JSON configuration file.</param>
/// <param name="optional">Determines if the configuration is optional.</param>
public SettingsConfigurationProvider(string name)
: this(name, false)
{
}
/// <summary>
/// Initializes a new instance of <see cref="SettingsConfigurationProvider"/>.
/// </summary>
/// <param name="name">Name of the JSON configuration file.</param>
/// <param name="optional">Determines if the configuration is optional.</param>
public SettingsConfigurationProvider(string name, bool optional)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("Name must be a non-empty string.", nameof(name));
}
this.Optional = optional;
this.Name = name;
}
/// <summary>
/// Gets a value that determines if this instance of <see cref="SettingsConfigurationProvider"/> is optional.
/// </summary>
public bool Optional { get; }
/// <summary>
/// The name of the file backing this instance of <see cref="SettingsConfigurationProvider"/>.
/// </summary>
public string Name { get; }
/// <summary>
/// Loads the contents of the embedded resource with name <see cref="Path"/>.
/// </summary>
/// <exception cref="FileNotFoundException">If <see cref="Optional"/> is <c>false</c> and a
/// resource does not exist with name <see cref="Path"/>.</exception>
public override void Load()
{
Assembly assembly = Assembly.GetAssembly(typeof(SettingsConfigurationProvider));
var resourceName = $"{assembly.GetName().Name}.{this.Name}";
var resources = assembly.GetManifestResourceNames();
if (!resources.Contains(resourceName))
{
if (Optional)
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
else
{
throw new FileNotFoundException($"The configuration file with name '{this.Name}' was not found and is not optional.");
}
}
else
{
using (Stream settingsStream = assembly.GetManifestResourceStream(resourceName))
{
Load(settingsStream);
}
}
}
internal void Load(Stream stream)
{
JsonConfigurationFileParser parser = new JsonConfigurationFileParser();
try
{
Data = parser.Parse(stream);
}
catch (JsonReaderException e)
{
string errorLine = string.Empty;
if (stream.CanSeek)
{
stream.Seek(0, SeekOrigin.Begin);
IEnumerable<string> fileContent;
using (var streamReader = new StreamReader(stream))
{
fileContent = ReadLines(streamReader);
errorLine = RetrieveErrorContext(e, fileContent);
}
}
throw new FormatException($"Could not parse the JSON file. Error on line number '{e.LineNumber}': '{e}'.");
}
}
private static string RetrieveErrorContext(JsonReaderException e, IEnumerable<string> fileContent)
{
string errorLine;
if (e.LineNumber >= 2)
{
var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList();
errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim();
}
else
{
var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault();
errorLine = possibleLineContent ?? string.Empty;
}
return errorLine;
}
private static IEnumerable<string> ReadLines(StreamReader streamReader)
{
string line;
do
{
line = streamReader.ReadLine();
yield return line;
} while (line != null);
}
}
}
You need also the JsonConfigurationFileParser:
namespace ClassLibrary
{
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
internal class JsonConfigurationFileParser
{
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string _currentPath;
private JsonTextReader _reader;
public IDictionary<string, string> Parse(Stream input)
{
_data.Clear();
_reader = new JsonTextReader(new StreamReader(input));
_reader.DateParseHandling = DateParseHandling.None;
var jsonConfig = JObject.Load(_reader);
VisitJObject(jsonConfig);
return _data;
}
private void VisitJObject(JObject jObject)
{
foreach (var property in jObject.Properties())
{
EnterContext(property.Name);
VisitProperty(property);
ExitContext();
}
}
private void VisitProperty(JProperty property)
{
VisitToken(property.Value);
}
private void VisitToken(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
VisitJObject(token.Value<JObject>());
break;
case JTokenType.Array:
VisitArray(token.Value<JArray>());
break;
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.String:
case JTokenType.Boolean:
case JTokenType.Bytes:
case JTokenType.Raw:
case JTokenType.Null:
VisitPrimitive(token);
break;
default:
throw new FormatException($#"
Unsupported JSON token '{_reader.TokenType}' was found.
Path '{_reader.Path}',
line {_reader.LineNumber}
position {_reader.LinePosition}.");
}
}
private void VisitArray(JArray array)
{
for (int index = 0; index < array.Count; index++)
{
EnterContext(index.ToString());
VisitToken(array[index]);
ExitContext();
}
}
private void VisitPrimitive(JToken data)
{
var key = _currentPath;
if (_data.ContainsKey(key))
{
throw new FormatException($"A duplicate key '{key}' was found.");
}
_data[key] = data.ToString();
}
private void EnterContext(string context)
{
_context.Push(context);
_currentPath = string.Join(Constants.KeyDelimiter, _context.Reverse());
}
private void ExitContext()
{
_context.Pop();
_currentPath = string.Join(Constants.KeyDelimiter, _context.Reverse());
}
}
}
If I have a class and I make it Serializable because I want to save the state. How do I get that state back?
[Serializable]
public class MyObject
{
public int a = 5;
}
Related post where I learned about Serialization What is [Serializable] and when should I use it?
If you want to change and save the state at runtime, a class with the Serializable attribute is the wrong way to go.
My guess is that the PlayerPrefs will be good enough for what you are looking for. You could do something like this:
public static string playerPrefKey_a = "a_value";
public static int a
{
get { return PlayerPrefs.GetInt(playerPrefKey_a); }
set { PlayerPrefs.SetInt(playerPrefKey_a, value); }
}
/// <summary>
/// (extension method) Deserializes a byte array into an object.
/// </summary>
public static T DeserializeToObject<T>(this byte[] buffer) where T : class
{
using (var stream = new MemoryStream(buffer))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
I Have a third party XML file that I need to deserialize into objects but when I do so I get an error: string was not recognized as a valid datetime. This is because the time in XML is just that a time, in the format HH:mm:SS and the classes that were Generated from the XSD from the third party produce a datetime field that expects a date time not just a time.
they give me the xml:
<PO>
...
<PurchaseOrderTime>8:00:00</PurchaseOrderTime>
...
</PO>
The Generated class creates a System.DateTime object to hold the deserialized PurchaseOrderTime but fails due to it expecting a format along the lines of yyyy/MM/dd HH:mm:SS tt but as I have no control over what they send me xsd or xml, what can I do to fix this?
Is there a way to pre-process the field to get what I need?
Do I have to manually change the System.DateTime to a timespan(there is more than just this one time field otherwise I would have just done that)
What is the best way to do this?
Edit 1:
Here is the generated class from the XSD
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.1015")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.spscommerce.com/RSX")]
[System.Xml.Serialization.XmlRootAttribute("OrderHeader", Namespace="http://www.spscommerce.com/RSX", IsNullable=false)]
public partial class OrderHeaderType : System.ComponentModel.INotifyPropertyChanged {
...
private System.DateTime purchaseOrderTimeField;
private bool purchaseOrderTimeFieldSpecified;
...
[System.Xml.Serialization.XmlElementAttribute(DataType="time", Order=7)]
public System.DateTime PurchaseOrderTime {
get {
return this.purchaseOrderTimeField;
}
set {
if ((purchaseOrderTimeField.Equals(value) != true)) {
this.purchaseOrderTimeField = value;
this.OnPropertyChanged("PurchaseOrderTime");
}
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool PurchaseOrderTimeSpecified {
get {
return this.purchaseOrderTimeFieldSpecified;
}
set {
if ((purchaseOrderTimeFieldSpecified.Equals(value) != true)) {
this.purchaseOrderTimeFieldSpecified = value;
this.OnPropertyChanged("PurchaseOrderTimeSpecified");
}
}
}
#region Serialize/Deserialize
/// <summary>
/// Serializes current OrderHeaderType object into an XML document
/// </summary>
/// <returns>string XML value</returns>
public virtual string Serialize() {
System.IO.StreamReader streamReader = null;
System.IO.MemoryStream memoryStream = null;
try {
memoryStream = new System.IO.MemoryStream();
Serializer.Serialize(memoryStream, this);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
streamReader = new System.IO.StreamReader(memoryStream);
return streamReader.ReadToEnd();
}
finally {
if ((streamReader != null)) {
streamReader.Dispose();
}
if ((memoryStream != null)) {
memoryStream.Dispose();
}
}
}
/// <summary>
/// Deserializes workflow markup into an OrderHeaderType object
/// </summary>
/// <param name="xml">string workflow markup to deserialize</param>
/// <param name="obj">Output OrderHeaderType object</param>
/// <param name="exception">output Exception value if deserialize failed</param>
/// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
public static bool Deserialize(string xml, out OrderHeaderType obj, out System.Exception exception) {
exception = null;
obj = default(OrderHeaderType);
try {
obj = Deserialize(xml);
return true;
}
catch (System.Exception ex) {
exception = ex;
return false;
}
}
public static bool Deserialize(string xml, out OrderHeaderType obj) {
System.Exception exception = null;
return Deserialize(xml, out obj, out exception);
}
public static OrderHeaderType Deserialize(string xml) {
System.IO.StringReader stringReader = null;
try {
stringReader = new System.IO.StringReader(xml);
return ((OrderHeaderType)(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
}
finally {
if ((stringReader != null)) {
stringReader.Dispose();
}
}
}
}
/// <summary>
/// Deserializes xml markup from file into an OrderHeaderType object
/// </summary>
/// <param name="fileName">string xml file to load and deserialize</param>
/// <param name="obj">Output OrderHeaderType object</param>
/// <param name="exception">output Exception value if deserialize failed</param>
/// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
public static bool LoadFromFile(string fileName, out OrderHeaderType obj, out System.Exception exception) {
exception = null;
obj = default(OrderHeaderType);
try {
obj = LoadFromFile(fileName);
return true;
}
catch (System.Exception ex) {
exception = ex;
return false;
}
}
public static bool LoadFromFile(string fileName, out OrderHeaderType obj) {
System.Exception exception = null;
return LoadFromFile(fileName, out obj, out exception);
}
public static OrderHeaderType LoadFromFile(string fileName) {
System.IO.FileStream file = null;
System.IO.StreamReader sr = null;
try {
file = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read);
sr = new System.IO.StreamReader(file);
string xmlString = sr.ReadToEnd();
sr.Close();
file.Close();
return Deserialize(xmlString);
}
finally {
if ((file != null)) {
file.Dispose();
}
if ((sr != null)) {
sr.Dispose();
}
}
}
#endregion
}
I Ended up doing a few things,
I used a refactoring tool to change all DateTimes in the generated classes to strings and and wrote the code in a way that it would work as if the DateTime was a string. (while this may not be optimal it works!)
Asked the people who generated the xml to fix the generated xml file. so that when/if I get a new file I can just regenerate the classes from the XSD and use the DateTime fields as expected.
While programing around this issue with strings was not the best thing, It should work either way and until I get a file that processes correctly I will just use the refactoring tool to change all DateTime's to strings.
If your user gives you a document which does not validate against the schema, XSD2Code is likely to have trouble with it. Throw it back at the user and tell them to fix it. Or parse the document and interpret it yourself, so you can write your own code to decide how to recover from questionable cases.
If the document does validate against the schema but XSD2Code can't handle it, complain to the authors of XSD2Code and get it fixed, or replace it with something which doesn't have the same bug/limitation. Which may, again, mean writing your own code.
If XSD2Code can handle it but C#'s provided code can't, you need to either go looking for code that can handle it (suggested websearch: c# parse iso 8601), or write your own.
I've used xsd.exe to generate a proxy class called "FacturaE".
I can instantiate this class, fill it information, and save to XML with XmlSerializer.
But, given a XML file, well formatted, how can I read it into a "FacturaE"?
Thanks
As a replacement for xsd.exe, Xsd2Code is a much more fully featured class generator. It can add methods to the generated classes to both serialize, and deserialize from Xml.
Following is an example of the serialize/deserialize methods generated by Xsd2Code:
/// <summary>
/// Serializes current EntityBase object into an XML document
/// </summary>
// <returns>string XML value</returns>
public virtual string Serialize() {
System.IO.StreamReader streamReader = null;
System.IO.MemoryStream memoryStream = null;
try {
memoryStream = new System.IO.MemoryStream();
Serializer.Serialize(memoryStream, this);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
streamReader = new System.IO.StreamReader(memoryStream);
return streamReader.ReadToEnd();
}
finally {
if (streamReader != null) {
streamReader.Dispose();
}
if (memoryStream != null) {
memoryStream.Dispose();
}
}
}
/// <summary>
/// Deserializes workflow markup into an EntityBase object
/// </summary>
// <param name="xml">string workflow markup to deserialize</param>
// <param name="obj">Output EntityBase object</param>
// <param name="exception">output Exception value if deserialize failed</param>
// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
public static bool Deserialize(string xml, out T obj, out System.Exception exception) {
exception = null;
obj = default(T);
try {
obj = Deserialize(xml);
return true;
}
catch (System.Exception ex) {
exception = ex;
return false;
}
}
public static bool Deserialize(string xml, out T obj) {
System.Exception exception = null;
return Deserialize(xml, out obj, out exception);
}
public static T Deserialize(string xml) {
System.IO.StringReader stringReader = null;
try {
stringReader = new System.IO.StringReader(xml);
return ((T)(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
}
finally {
if (stringReader != null) {
stringReader.Dispose();
}
}
}
I have a very simple WCF program where I have a simple self-host and a client running on the same computer. There is a single method which returns a System.IO.Stream, which is actually a serialized form of a simple string. (This could be any number of data types, but for the time being let's take it as a string).
Following is the code I use if you want to take a look at. SerializeData() and DeserializeData() are methods used to do just that, and works fine.
Host Service:
namespace HelloWCF1
{
[ServiceContract(Namespace = "http://My.WCF.Samples")]
public interface IService1
{
[OperationContract]
Stream GetString();
}
public class Service1 : IService1
{
//Simple String
public Stream GetString()
{
string str = "ABC";
Stream ms = new MemoryStream();
SerializeData<string>(str, ms);
return ms;
}
/// <summary>
/// Serialize an object of the type T to a Stream
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectToSerialize"></param>
/// <param name="str"></param>
public void SerializeData<T>(T objectToSerialize, Stream str)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
bf.Serialize(str, objectToSerialize);
str.Position = 0;
}
catch (Exception)
{
}
}
/// <summary>
/// Deserialize a Stream
/// </summary>
/// <param name="dataToDeserialize"></param>
/// <returns></returns>
public object DeserializeData(Stream dataToDeserialize)
{
BinaryFormatter bf = new BinaryFormatter();
object ret = null;
try
{
ret = bf.Deserialize(dataToDeserialize);
}
catch (Exception)
{
}
return ret;
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddr = new Uri("http://localhost:8000/WCFSampleService");
//ServiceHost is created by defining Service Type and Base Address
using (ServiceHost svcHost = new ServiceHost(typeof(Service1), baseAddr))
{
//Trace message for service start
Console.WriteLine("Service Starting...");
//Adding an end point
svcHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "HelloWCF");
//Open service host
svcHost.Open();
Console.WriteLine("Press [Enter] to terminate.");
Console.ReadLine();
//Close service host
svcHost.Close();
}
}
}
}
Client Program:
namespace Client
{
[ServiceContract(Namespace = "http://My.WCF.Samples")]
public interface IService1
{
[OperationContract]
Stream GetString();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private IService1 proxy = null;
private Stream memStr = new MemoryStream();
private void button1_Click(object sender, EventArgs e)
{
//Create end point
EndpointAddress epAddr = new EndpointAddress("http://localhost:8000/WCFSampleService/HelloWCF");
//Create proxy
proxy = ChannelFactory<IService1>.CreateChannel(new BasicHttpBinding(), epAddr);
//WCF Service Method is called to aquire the stream
try
{
memStr = proxy.GetString();
string str = (string)DeserializeData(memStr);
MessageBox.Show(str);
}
catch (CommunicationException commEx)
{
MessageBox.Show("Service Call Failed:" + commEx.Message);
}
}
/// <summary>
/// Serialize an object of the type T to a Stream
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectToSerialize"></param>
/// <param name="str"></param>
public static void SerializeData<T>(T objectToSerialize, Stream str)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
bf.Serialize(str, objectToSerialize);
str.Position = 0;
}
catch (Exception)
{
}
}
/// <summary>
/// Deserialize a Stream
/// </summary>
/// <param name="dataToDeserialize"></param>
/// <returns></returns>
public static object DeserializeData(Stream dataToDeserialize)
{
BinaryFormatter bf = new BinaryFormatter();
object ret = null;
try
{
ret = bf.Deserialize(dataToDeserialize);
}
catch (Exception)
{
}
return ret;
}
}
}
Now, this works fine. It serializes a string into a Stream and sends using HTTP quite fine. However, I have a need to convert of cast this Stream into an object before sending. Simply put, I need the GetString() method to look like this:
public object GetString()
{
string str = "ABC";
Stream ms = new MemoryStream();
SerializeData<string>(str, ms);
return (object)ms;
}
However, when I do this, inside the Button1_Click event, at the line ***memStr = proxy.GetString();*** I get a CommunicationException. I work in a Japanese OS, so the exception message I get is in Japanese so I'll try to translate into English as best as I can. Forgive me if it is not very clear.
***Error occured in the reception of HTTP response concerning [url]http://localhost:8000/WCFSampleService/HelloWCF[/url]. Cause of the error could be Service End Point binding is not using an HTTP protocol. Or as a different cause, it is also possible that HTTP request context is suspended according to the server (as in the case of server being shut down). Please refer the server log for more details.***
What exactly is the problem and how can I get the program to work the way I want? It says to look at the log files but where can I find them?
Thanks in advance!
Would you not be better off just returning the string? Surely the string returned as a stream over an Http binding will be being Base64 encoded, which normally doubles the size of the data being returned!
Maybe you could consider TcpBinding as an alternative.