Import Data from Excel 2003 file into ASP DataTable using Interop - asp.net

I'm trying to import tariff information from an Excel file into a DataTable to bind to a GridView. I'm limited to 2003 right now, and for the foreseeable future. From what I gather there are two methods.
One querying the excel sheet using ADO SELECT Code, Description FROM [Sheet1$]
With this connection string Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\file.xls;Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\
Problem with this: Jet for some reason truncates the cell length at 255 characters. The tariff descriptions can be around 900 sometimes. I've read into this, and can't seem to find a solution. Some people say it's because the description is stored as Text and not Memo, but I can't seem to fix it.
OR
The other solution I've found is using Microsoft.Office.Interop.Excel. It works and grabs all of the Description field, but the code is very ugly. It runs through each cell of each row of the worksheet and copies the text value into a DataRow, then appends it onto a DataTable.
// Set Columns
for (int i = 1; i <= columnCount; i++) {
Range columnRange = (Range)oSheet.Cells[1, i];
dt.Columns.Add(columnRange.Text.ToString());
}
// Add Rows
for (int i = 2; i <= rowCount; i++) {
dr = dt.NewRow();
for (int j = 1; j <= columnCount; j++) {
oRng = (Range)oSheet.Cells[i, j];
dr[j - 1] = oRng.Text.ToString().Trim();
}
Problem with this: very slow, cause of the nested loop.
Is there a faster way to get the data into a DataTable using Interop? Or a way to prevent the truncation of 255 characters using the query?

You could have a look at the Excel Data Reader library. I've had pretty good luck with it and I think it will do what you're looking for pretty easily.

This doesn't use the Excel interop, but you can give this a shot. It contains methods to convert and Excel document into a DataTable.
If possible, you should consider using the OpenXML SDK for this, but if you can't this is probably a good option for you.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.OleDb;
using System.ComponentModel;
namespace Excel
{
public class ExcelBase : Component, IDisposable
{
#region Constructors
public ExcelBase()
{
UseFinalizer = false;
}
public ExcelBase(string WorkBook) : this()
{
this.WorkBook = WorkBook;
}
#endregion
#region Workbook/range settings
string workbook;
/// <summary>
/// The workbook (file) name to query
/// </summary>
[DefaultValue(null)]
public string WorkBook
{
get { return workbook; }
set
{
CloseConnection();
workbook = value;
determinedrange = null;
}
}
/// <summary>
/// The Range which to query. This can be any Excel range (eg "A1:B5") or
/// just a worksheet name.
/// If this value is null, the first sheet of the <see cref="WorkBook"/> is used
/// </summary>
private string range;
[DefaultValue(null)]
public string Range
{
get { return range; }
set
{
range = value;
determinedrange = null;
}
}
private int worksheetindex = 0;
/// <summary>
/// The 0 based INDEX of the worksheet to query.
/// If you want to set the name of the worksheet, use <see cref="Range"/> instead.
/// NB: if <see cref="Range"/> is set, this property is ignored
/// </summary>
[DefaultValue(0)]
public int WorkSheetIndex
{
get { return worksheetindex; }
set
{
worksheetindex = value;
determinedrange = null;
}
}
#region Range formatting
/// <summary>
/// If a range was determined in a previous step, keep it buffered here
/// </summary>
string determinedrange;
/// <summary>
/// Gets the properly formatted sheet name
/// if no worksheet was provided, read out sheet information and select everything
/// from the first sheet
/// </summary>
public string GetRange()
{
if (determinedrange == null)
{
string range = Range;
if (range == null)
range = DetermineRange();
if (range.IndexOf(':') == -1 && !range.EndsWith("$"))
range += "$"; //sheetname has to be appended with a $
determinedrange = "[" + range + "]";
}
return determinedrange;
}
/// <summary>
/// See <see cref="AutoDetermineRange"/> property for more info
/// </summary>
/// <returns></returns>
string DetermineRange()
{
string sheet = GetSheetName(worksheetindex);
if (!autodeterminerange) return sheet;
return new RangeFinder(this, sheet).ToString();
}
#region RangeFinder
class RangeFinder
{
OleDbDataAdapter da;
DataTable dtSchema;
ExcelDataRange rng = new ExcelDataRange();
Import eb;
int cols;
/// <summary>
/// minimum amount of columns that need to be filled
/// <seealso cref="minfilled"/>
/// </summary>
int min;
public RangeFinder(ExcelBase Owner, string sheet)
{
this.eb = new Import(Owner.WorkBook);
eb.Range = sheet;
eb.UseHeaders = false;
eb.InterMixedAsText = true;
//DataTable dt = eb.Query();
try
{
eb.OpenConnection();
//get the number of rows and columns
da = new OleDbDataAdapter(
"select * from [" + sheet + "]", eb.Connection);
dtSchema = new DataTable();
da.FillSchema(dtSchema, SchemaType.Source);
cols = dtSchema.Columns.Count;
int rows = (int)ExecuteScalar("select count(*) from [" + sheet + "]");
//fill the range object
rng.From.Row = rng.From.Column = 1;
rng.To.Row = rows;
rng.To.Column = cols;
min = (int)(cols * minfilled);
//now rng contains the complete square range of data containing cells
//try to narrow it by getting as much hits as possible
DecreaseRange();
}
finally
{
indexReader.Close();
eb.CloseConnection();
}
}
object ExecuteScalar(string sql)
{
return new OleDbCommand(sql, da.SelectCommand.Connection).ExecuteScalar();
}
string indexquery;
string GetIndexQuery()
{
if (indexquery == null)
{
StringBuilder sql = new StringBuilder("select 0");
int i = 0;
foreach (DataRow dr in dtSchema.Rows)
{
string colname = "[" + dr["column_name"].ToString() + "]";
sql.Append("+iif(").Append(colname).Append(" is null,0,1)");
}
sql.Append(" as ind from ");
indexquery = sql.ToString();
}
return indexquery;
}
//ExcelDataRange indexRange;
DataTable indexTable = new DataTable();
OleDbDataReader indexReader;
int GetIndex()
{
if (!Forward)
{
indexReader.Close();
indexReader = null;
da.SelectCommand.CommandText = string.Format(" select * from {0}:{0}"
, rng.To.Row);
}
if (indexReader == null)
indexReader = da.SelectCommand.ExecuteReader();
int cnt = 0;
if (!indexReader.Read()) return -1;
for (int i = 0; i < indexReader.FieldCount; i++)
{
if (!indexReader.IsDBNull(i)) cnt++;
}
return cnt;
da.TableMappings.Clear();
da = new OleDbDataAdapter(da.SelectCommand.CommandText, eb.conn);
indexTable = new DataTable();
//da.FillSchema(indexTable, SchemaType.Source);
da.Fill(indexTable);
return indexTable.Columns.Count;
}
/// <summary>
/// minimum percentage that needs to be filled to count as a datarow
/// </summary>
const double minfilled = .75;
/// <summary>
/// The amount of subsequent (or preceding) rows that need to be filled a <see cref="minfilled"/> percentage
/// for it to count as a datarow
/// </summary>
const int CheckRows = 3;
/// <summary>
/// Decrease the range step by step
/// The problem is that when obtaining all, a lot more nulls are returned
/// than you would visibly see. That makes most algorithms to get the
/// block useless.
/// this is also why just obtaining the datatable complete and removing the
/// rows will not suffice: the proper field data types will not have been set
/// Best way I could figure without using interop was to increase the start
/// range to see if the avarage filled values increase.
/// </summary>
void DecreaseRange()
{
for (; ; )
{
if (GetIndex() >= min)
{
int i = 0;
for (; i < CheckRows; i++)
{
AlterRange(1);
if (GetIndex() < min)
{
break;
}
}
if (i == CheckRows)
{
AlterRange(-i);
if (Forward)
Forward = false;
else
break;
}
}
if (rng.From.Row > rng.To.Row)
throw new Exception("Could not determine data range");
AlterRange(1);
}
}
bool Forward = true;
void AlterRange(int i)
{
if (Forward)
rng.From.Row += i;
else
rng.To.Row -= i;
}
public override string ToString()
{
return rng.ToString();
}
struct ExcelRange
{
public int Row, Column;
public ExcelRange(int Col, int Row)
{
this.Column = Col;
this.Row = Row;
}
public override string ToString()
{
//return string.Format("R{0}C{1}", Row, Column);
string res = Row.ToString();
int col = Column;
while (col > 0)
{
int cc = col % 26;
char c = (char)('A' + cc - 1);
res = c.ToString() + res;
col /= 26;
}
return res;
}
}
struct ExcelDataRange
{
public ExcelRange
From, To;
public override string ToString()
{
return GetRange(From, To);
}
static string GetRange(ExcelRange from, ExcelRange to)
{
return from.ToString() + ":" + to.ToString();
}
public string TopRow()
{
return GetRange(From, new ExcelRange(To.Column, From.Row));
}
public string BottomRow()
{
return GetRange(new ExcelRange(From.Column, To.Row), To);
}
}
}
#endregion
#endregion
/// <summary>
/// Checks if the <see cref="WorkBook"/> exists
/// </summary>
public bool WorkBookExists
{
get { return System.IO.File.Exists(WorkBook); }
}
/// <summary>
/// Checks if the workbook exists and throws an exception if it doesn't
/// <seealso cref="WorkBookExists"/>
/// </summary>
protected void CheckWorkbook()
{
if (!WorkBookExists) throw new System.IO.FileNotFoundException("Workbook not found", WorkBook);
}
#endregion
#region Connection
/// <summary>
/// Creates a NEW connection. If this method is called directly, this
/// class will not check if it is closed.
/// To get a handled connection, use the <see cref="Connection"/> property.
/// </summary>
/// <returns></returns>
public OleDbConnection CreateConnection()
{
CheckWorkbook();
return new OleDbConnection(
string.Format("Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data Source={0};Extended Properties='Excel 8.0;HDR={1};Imex=1'",
WorkBook, useheaders ? "Yes" : "No"));
}
private bool useheaders = true;
/// <summary>
/// Determines if the first row in the specified <see cref="Range"/> contains the headers
/// </summary>
[DefaultValue(true)]
public bool UseHeaders
{
get { return useheaders; }
set
{
if (useheaders != value)
{
CloseConnection();
useheaders = value;
}
}
}
private bool imex;
/// <summary>
/// if this value is <c>true</c>, 'intermixed' data columns are handled as text (otherwise Excel tries to make a calcuated guess on what the datatype should be)
/// </summary>
[DefaultValue(false)]
public bool InterMixedAsText
{
get { return imex; }
set
{
if (imex != value)
{
CloseConnection();
imex = value;
}
}
}
private bool autodeterminerange;
/// <summary>
/// Tries to obtain the range automatically by looking for a large chunk of data. Use this value if there's a lot of
/// static around the actual data.
/// Beware though: this takes some additional steps and can cause performance loss
/// when querying larger files.
/// automatically determening the range is not fullproof. Be sure to check the results
/// on first time use.
/// NB: if the <see cref="Range"/> is set, this property is ignored.
/// </summary>
[DefaultValue(false)]
public bool AutoDetermineRange
{
get { return autodeterminerange; }
set
{
if (autodeterminerange != value)
{
autodeterminerange = value;
determinedrange = null;
}
}
}
OleDbConnection conn;
/// <summary>
/// Gets a connection to the current <see cref="WorkBook"/>
/// When called for the first time (or after changing the workbook)
/// a new connection is created.
/// To close the connection, preferred is the use of <see cref="CloseConnection"/>
/// </summary>
public OleDbConnection Connection
{
get
{
if (conn == null)
{
conn = CreateConnection();
UseFinalizer = true;
}
return conn;
}
}
/// <summary>
/// Closes the connection (if open)
/// </summary>
public void CloseConnection()
{
if (conn != null && ConnectionIsOpen)
conn.Dispose();
conn = null;
UseFinalizer = false;
}
protected void CloseConnection(bool OnlyIfNoneOpen)
{
if (OnlyIfNoneOpen)
{
if (--opencount > 0 || wasopenbeforerememberstate) return;
}
CloseConnection();
}
/// <summary>
/// Opens the <see cref="Connection"/>
/// </summary>
public void OpenConnection()
{
OpenConnection(false);
}
int opencount;
bool wasopenbeforerememberstate;
protected void OpenConnection(bool RememberState)
{
if (RememberState && opencount++ == 0) wasopenbeforerememberstate = ConnectionIsOpen;
if (!ConnectionIsOpen)
Connection.Open();
}
public bool ConnectionIsOpen
{
get { return conn != null && conn.State != ConnectionState.Closed; }
}
#endregion
#region IDisposable Members
public void Dispose()
{
CloseConnection();
}
~ExcelBase()
{
Dispose();
}
private bool usefinalizer;
bool UseFinalizer
{
get { return usefinalizer; }
set
{
if (usefinalizer == value) return;
usefinalizer = value;
if (value)
GC.ReRegisterForFinalize(this);
else
GC.SuppressFinalize(this);
}
}
#endregion
#region Helper functions
/// <summary>
/// queries the connection for the sheetnames and returns them
/// </summary>
/// <returns></returns>
public string[] GetSheetNames()
{
OpenConnection(true);
try
{
// Read out sheet information
DataTable dt = Connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null || dt.Rows.Count == 0)
{
throw new Exception("Could not get sheet names");
}
string[] res = new string[dt.Rows.Count];
for (int i = 0; i < res.Length; i++)
{
string name = dt.Rows[i]["TABLE_NAME"].ToString();
if (name[0] == '\'')
{
//numeric sheetnames get single quotes around them in the schema.
//remove them here
if (System.Text.RegularExpressions.Regex.IsMatch(
name, #"^'\d\w+\$'$"))
name = name.Substring(1, name.Length - 2);
}
res[i] = name;
}
return res;
}
finally
{
CloseConnection(true);
}
}
/// <summary>
/// Gets the name of the first sheet
/// (this is also the default range used, when no <see cref="Range"/> is specified)
/// </summary>
/// <returns></returns>
public string GetFirstSheet()
{
return GetSheetName(0);
}
public string GetSheetName(int index)
{
string[] sheets = GetSheetNames();
if (index < 0 || index >= sheets.Length)
throw new IndexOutOfRangeException("No worksheet exists at the specified index");
return sheets[index];
}
#endregion
}
public class Import : ExcelBase
{
#region Static query procedures
/// <summary>
/// Imports the first worksheet of the specified file
/// </summary>
/// <param name="File"></param>
public static DataTable Query(string File)
{
return Query(File, null);
}
/// <summary>
/// Imports the specified sheet in the specified file
/// </summary>
/// <param name="File"></param>
/// <param name="Range">The worksheet or excel range to query</param>
/// <returns></returns>
public static DataTable Query(string File, string Range)
{
return new Import(File, Range).Query();
}
public static DataTable Select(string File, string Sql)
{
Import i = new Import(File);
i.SQL = Sql;
return i.Query();
}
#endregion
#region Constructors
public Import() { }
public Import(string WorkBook) : base(WorkBook) { }
public Import(string WorkBook, string Range)
: this(WorkBook)
{
this.Range = Range;
}
#endregion
#region SQL Query
private string fields = "*";
/// <summary>
/// The fields which should be returned (default all fields with data: "*")
/// </summary>
[DefaultValue("*")]
public string Fields
{
get { return fields; }
set { fields = value; }
}
void ResetFields()
{
fields = "*";
}
private string where;
/// <summary>
/// An optional where clause. Works pretty much the same as 'normal' SQL. (Default=null)
/// </summary>
[DefaultValue(null)]
public string Where
{
get { return where; }
set { where = value; }
}
/// <summary>
/// The sql to perform. If this value is filled, <see cref="WorkSheet"/> and <see cref="Where"/> are ignored
/// </summary>
public string SQL;
protected string GetSelectSQL()
{
if (SQL != null) return SQL;
// if no sql was provided, construct from worksheet and where
string sql = string.Format("select {0} from {1}", fields, GetRange());
if (where != null)
sql += " WHERE " + where;
return sql;
}
/// <summary>
/// Performs the query with the specifed settings
/// </summary>
/// <returns></returns>
public DataTable Query()
{
return Query((DataTable)null);
}
/// <summary>
/// Same as <see cref="Query()"/>, but an existing datatable is used and filled
/// (it will be your own responsibility to format the datatable correctly)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public DataTable Query(DataTable dt)
{
CheckWorkbook();
try
{
OpenConnection(true);
if (dt == null) dt = new DataTable();
new OleDbDataAdapter(GetSelectSQL(), Connection).Fill(dt);
foreach (DataColumn col in dt.Columns)
col.ColumnName = col.ColumnName.Trim();
return dt;
}
finally
{
CloseConnection(true);
}
}
/// <summary>
/// Fills the datatable with the results of the query
/// (wrapper around <see cref="Query(DataTable)"/>)
/// </summary>
/// <param name="dt"></param>
public void Fill(DataTable dt)
{
Query(dt);
}
#endregion
}
}

Related

Blazor multi-user questionaire

I need to create an app thats basically functions like a multi-user realtime questionnaire or trivia.
I've gotten to the point where i've created a questionnaire and associated users to it, but i'm hitting a road block on how to go forward.
Basically what i need to do next is
For each question in the questionnaire:
1) have the server send the users a question
2) wait for all of the users to respond or a set timeout period
3) display to the users a question result page
I was wondering how i should attempt to do something like this. Any help or resources would be wonderful
I am building a chat application that has something very similar using Server Side Blazor.
The way I did was I inject a class I created called SubscriberService:
#inject Services.SubscriberService SubscriberService
Then in my ConfigureServices method in Startup.cs I add this:
services.AddSingleton<SubscriberService>();
The Add Singleton means only 1 instance will be created for all your browser instances (users).
This makes my Subscriber Services available to all my subcribers, which is just a name, Guid Id and a callback delegate
#region using statements
using DataJuggler.UltimateHelper.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
#endregion
namespace BlazorChat.Services
{
#region class SubscriberService
/// <summary>
/// This class is used to subscribe to services, so other windows get a notification a new message
/// came in.
/// </summary>
public class SubscriberService
{
#region Private Variables
private int count;
private Guid serverId;
private List<SubscriberCallback> subscribers;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a 'SubscriberService' object.
/// </summary>
public SubscriberService()
{
// Create a new Guid
this.ServerId = Guid.NewGuid();
Subscribers = new List<SubscriberCallback>();
}
#endregion
#region Methods
#region BroadcastMessage(SubscriberMessage message)
/// <summary>
/// This method Broadcasts a Message to everyone that ins't blocked.
/// Note To Self: Add Blocked Feature
/// </summary>
public void BroadcastMessage(SubscriberMessage message)
{
// if the value for HasSubscribers is true
if ((HasSubscribers) && (NullHelper.Exists(message)))
{
// Iterate the collection of SubscriberCallback objects
foreach (SubscriberCallback subscriber in Subscribers)
{
// if the Callback exists
if ((subscriber.HasCallback) && (subscriber.Id != message.FromId))
{
// to do: Add if not blocked
// send the message
subscriber.Callback(message);
}
}
}
}
#endregion
#region GetSubscriberNames()
/// <summary>
/// This method returns a list of Subscriber Names ()
/// </summary>
public List<string> GetSubscriberNames()
{
// initial value
List<string> subscriberNames = null;
// if the value for HasSubscribers is true
if (HasSubscribers)
{
// create the return value
subscriberNames = new List<string>();
// Get the SubscriberNamesl in alphabetical order
List<SubscriberCallback> sortedNames = Subscribers.OrderBy(x => x.Name).ToList();
// Iterate the collection of SubscriberService objects
foreach (SubscriberCallback subscriber in sortedNames)
{
// Add this name
subscriberNames.Add(subscriber.Name);
}
}
// return value
return subscriberNames;
}
#endregion
#region Subscribe(string subscriberName)
/// <summary>
/// method returns a message with their id
/// </summary>
public SubscriberMessage Subscribe(SubscriberCallback subscriber)
{
// initial value
SubscriberMessage message = null;
// If the subscriber object exists
if ((NullHelper.Exists(subscriber)) && (HasSubscribers))
{
// Add this item
Subscribers.Add(subscriber);
// return a test message for now
message = new SubscriberMessage();
// set the message return properties
message.FromName = "Subscriber Service";
message.FromId = ServerId;
message.ToName = subscriber.Name;
message.ToId = subscriber.Id;
message.Data = Subscribers.Count.ToString();
message.Text = "Subscribed";
}
// return value
return message;
}
#endregion
#region Unsubscribe(Guid id)
/// <summary>
/// This method Unsubscribe
/// </summary>
public void Unsubscribe(Guid id)
{
// if the value for HasSubscribers is true
if ((HasSubscribers) && (Subscribers.Count > 0))
{
// attempt to find this callback
SubscriberCallback callback = Subscribers.FirstOrDefault(x => x.Id == id);
// If the callback object exists
if (NullHelper.Exists(callback))
{
// Remove this item
Subscribers.Remove(callback);
// create a new message
SubscriberMessage message = new SubscriberMessage();
// set the message return properties
message.FromId = ServerId;
message.FromName = "Subscriber Service";
message.Text = callback.Name + " has left the conversation.";
message.ToId = Guid.Empty;
message.ToName = "Room";
// Broadcast the message to everyone
BroadcastMessage(message);
}
}
}
#endregion
#endregion
#region Properties
#region Count
/// <summary>
/// This property gets or sets the value for 'Count'.
/// </summary>
public int Count
{
get { return count; }
set { count = value; }
}
#endregion
#region HasSubscribers
/// <summary>
/// This property returns true if this object has a 'Subscribers'.
/// </summary>
public bool HasSubscribers
{
get
{
// initial value
bool hasSubscribers = (this.Subscribers != null);
// return value
return hasSubscribers;
}
}
#endregion
#region ServerId
/// <summary>
/// This property gets or sets the value for 'ServerId'.
/// </summary>
public Guid ServerId
{
get { return serverId; }
set { serverId = value; }
}
#endregion
#region Subscribers
/// <summary>
/// This property gets or sets the value for 'Subscribers'.
/// </summary>
public List<SubscriberCallback> Subscribers
{
get { return subscribers; }
set { subscribers = value; }
}
#endregion
#endregion
}
#endregion
}
Here is my SubscriberCallback.cs:
#region using statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
#endregion
namespace BlazorChat
{
#region class SubscriberCallback
/// <summary>
/// This class is used to register a subscriber with the ChatService
/// </summary>
public class SubscriberCallback
{
#region Private Variables
private string name;
private Guid id;
private Callback callback;
private List<Guid> blockedList;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a SubscriberCallback instance
/// </summary>
public SubscriberCallback(string name)
{
// store the Name
Name = name;
// Create the Id
Id = Guid.NewGuid();
// create a BlockedList
BlockedList = new List<Guid>();
}
#endregion
#region Methods
#region ToString()
/// <summary>
/// This method is used to return the Name of the Subscriber when ToString is called.
/// </summary>
/// <returns></returns>
public override string ToString()
{
// return the Name when ToString is called
return this.Name;
}
#endregion
#endregion
#region Properties
#region BlockedList
/// <summary>
/// This property gets or sets the value for 'BlockedList'.
/// </summary>
public List<Guid> BlockedList
{
get { return blockedList; }
set { blockedList = value; }
}
#endregion
#region Callback
/// <summary>
/// This property gets or sets the value for 'Callback'.
/// </summary>
public Callback Callback
{
get { return callback; }
set { callback = value; }
}
#endregion
#region HasBlockedList
/// <summary>
/// This property returns true if this object has a 'BlockedList'.
/// </summary>
public bool HasBlockedList
{
get
{
// initial value
bool hasBlockedList = (this.BlockedList != null);
// return value
return hasBlockedList;
}
}
#endregion
#region HasCallback
/// <summary>
/// This property returns true if this object has a 'Callback'.
/// </summary>
public bool HasCallback
{
get
{
// initial value
bool hasCallback = (this.Callback != null);
// return value
return hasCallback;
}
}
#endregion
#region HasName
/// <summary>
/// This property returns true if the 'Name' exists.
/// </summary>
public bool HasName
{
get
{
// initial value
bool hasName = (!String.IsNullOrEmpty(this.Name));
// return value
return hasName;
}
}
#endregion
#region Id
/// <summary>
/// This property gets or sets the value for 'Id'.
/// </summary>
public Guid Id
{
get { return id; }
set { id = value; }
}
#endregion
#region Name
/// <summary>
/// This property gets or sets the value for 'Name'.
/// </summary>
public string Name
{
get { return name; }
set { name = value; }
}
#endregion
#endregion
}
#endregion
}
And here is my delegate class:
/// <summary>
/// This delegate is used by the SubscriberService to send messages to any subscribers
/// </summary>
/// <returns></returns>
public delegate void Callback(SubscriberMessage message);
Then in my component I call methods like this:
// Send this message to all clients
SubscriberService.BroadcastMessage(message);
And each client has a listen method:
SubscriberCallback callback = new SubscriberCallback(SubscriberName);
callback.Callback = Listen;
callback.Name = SubscriberName;
// Get a message back
SubscriberMessage message = SubscriberService.Subscribe(callback);
Here is my Listen method, it just waits for messages;
using DataJuggler.UltimateHelper.Core; // Nuget package
public void Listen(SubscriberMessage message)
{
// if the message exists (part of DataJuggler.UltimateHelper.Core Nuget Package)
// Same as (message != null)
if (NullHelper.Exists(message))
{
// if the message contains Joined the conversation
if ((message.Text.Contains("joined the conversation")) ||
(message.Text.Contains("left the conversation")))
{
// this updates my list of 'Whose On' whenever a user joins or leaves
// Get the Names again
this.Names = SubscriberService.GetSubscriberNames();
// Update the UI
Refresh();
}
else
{
// my display message code is here
}
}
And finally here is my Subscriber message:
#region using statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#endregion
namespace BlazorChat
{
#region class SubscriberMessage
/// <summary>
/// This class is used to send information between components / pages.
/// </summary>
public class SubscriberMessage
{
#region Private Variables
private string text;
private Guid fromId;
private Guid toId;
private string fromName;
private string toName;
private object data;
private string valid;
private DateTime sent;
private string invalidReason;
#endregion
#region Properties
#region Data
/// <summary>
/// This property gets or sets the value for 'Data'.
/// </summary>
public object Data
{
get { return data; }
set { data = value; }
}
#endregion
#region FromId
/// <summary>
/// This property gets or sets the value for 'FromId'.
/// </summary>
public Guid FromId
{
get { return fromId; }
set { fromId = value; }
}
#endregion
#region FromName
/// <summary>
/// This property gets or sets the value for 'FromName'.
/// </summary>
public string FromName
{
get { return fromName; }
set { fromName = value; }
}
#endregion
#region HasText
/// <summary>
/// This property returns true if the 'Text' exists.
/// </summary>
public bool HasText
{
get
{
// initial value
bool hasText = (!String.IsNullOrEmpty(this.Text));
// return value
return hasText;
}
}
#endregion
#region InvalidReason
/// <summary>
/// This property gets or sets the value for 'InvalidReason'.
/// </summary>
public string InvalidReason
{
get { return invalidReason; }
set { invalidReason = value; }
}
#endregion
#region Sent
/// <summary>
/// This property gets or sets the value for 'Sent'.
/// </summary>
public DateTime Sent
{
get { return sent; }
set { sent = value; }
}
#endregion
#region Text
/// <summary>
/// This property gets or sets the value for 'Text'.
/// </summary>
public string Text
{
get { return text; }
set { text = value; }
}
#endregion
#region ToId
/// <summary>
/// This property gets or sets the value for 'ToId'.
/// </summary>
public Guid ToId
{
get { return toId; }
set { toId = value; }
}
#endregion
#region ToName
/// <summary>
/// This property gets or sets the value for 'ToName'.
/// </summary>
public string ToName
{
get { return toName; }
set { toName = value; }
}
#endregion
#region Valid
/// <summary>
/// This property gets or sets the value for 'Valid'.
/// </summary>
public string Valid
{
get { return valid; }
set { valid = value; }
}
#endregion
#endregion
}
#endregion
}
BlazorChat is a sample project I am still working on as part of my Nuget package:
DataJuggler.Blazor.Components, which contains a Sprite component, ProgressBar and Validation component.
The full code is here in the Samples folder of this project if I left anything out.
https://github.com/DataJuggler/DataJuggler.Blazor.Components
Documentation and SQL scripts are missing, so sorry, its a work in progress.

can i find out the orignial value of a control with ViewState

In my ASP.NET web form, say I have something like this:
<asp:Literal ID="Literal1" runat="server" Text="abc"></asp:Literal>
Now say I change the text in the code behind. The control has viewstate enabled and so the new text value persists throughout postbacks which is exactly what I want. But in some scenarios I want to reset the control back to the original value (i.e. "abc") without having to hardcode it in the code behind. So without disabling viewstate, is there any way I can retrieve the original value that the control had when the screen was first loaded?
Thanks
You could try to get the text from the control in the Page_Init method. The viewstate is restored to the control tree later in the page processing pipeline. So if you access the text in Page_Init and store the value in some suitable place you can get the value from markup (aspx) file.
I dont know how to do it with viewstate. but you could create a base page for your site with options to track changes and store the default values when the page is loaded. And a class to do the actual checking.
for example:
Change Tracker:
/// <summary>
/// Class to Track changes in a web page
/// </summary>
[Serializable]
public class ChangeTracker : IDisposable
{
//- MEMBER VARIABLES --------------------------------------------------------------------------------------------------------
#region Members
private bool m_ChangesMade = false;
private Dictionary<string, object> m_OriginalValues = new Dictionary<string, object>();
private Dictionary<string, object> m_ChangedValues = new Dictionary<string, object>();
private List<string> m_ControlsToIgnore = new List<string>();
#endregion Members
//- PROPERTIES --------------------------------------------------------------------------------------------------------------
#region Properties
/// <summary>
/// Gets or Sets the Controls to Ignore (ClientID array)
/// </summary>
public List<string> ControlsToIgnore
{
get { return m_ControlsToIgnore; }
set { m_ControlsToIgnore = value; }
}
/// <summary>
/// Gets or Sets the Dictionay of Changed Values
/// </summary>
public Dictionary<string, object> ChangedValues
{
get { return m_ChangedValues; }
set { m_ChangedValues = value; }
}
/// <summary>
/// Gets or Sets the Dictionay of Original Values
/// </summary>
public Dictionary<string, object> OriginalValues
{
get { return m_OriginalValues; }
set { m_OriginalValues = value; }
}
/// <summary>
/// Gets or Sets Whether changes have been made to the control collection
/// </summary>
public bool ChangesMade
{
get
{
try
{
return m_ChangesMade;
}
catch (Exception)
{
throw;
}
}
set { m_ChangesMade = value; }
}
#endregion Properties
#region Constructor
/// <summary>
/// Constructor (required for serialization)
/// </summary>
public ChangeTracker()
{
}
#endregion Constructor
#region Methods
/// <summary>
/// Adds all relivant controls recursively to the list
/// </summary>
/// <param name="oControl">The control to add</param>
private void AddControls(Control oControl)
{
try
{
if (oControl != null)
{
//Check whether the control shoudl be ignored
if (!this.ControlsToIgnore.Contains(oControl.ClientID))
{
//loop recursively
foreach (Control aControl in oControl.Controls)
{
this.AddControls(aControl);
}
if (oControl is IEditableTextControl)
{
if (this.OriginalValues.ContainsKey(oControl.ClientID))
{
this.OriginalValues[oControl.ClientID] = (oControl as IEditableTextControl).Text;
}
else
{
this.OriginalValues.Add(oControl.ClientID, (oControl as IEditableTextControl).Text);
}
}
if (oControl is ICheckBoxControl)
{
if (this.OriginalValues.ContainsKey(oControl.ClientID))
{
this.OriginalValues[oControl.ClientID] = (oControl as ICheckBoxControl).Checked;
}
else
{
this.OriginalValues.Add(oControl.ClientID, (oControl as ICheckBoxControl).Checked);
}
}
}
}
else
{
throw new ArgumentNullException("oControl");
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Checks the controls for changes
/// </summary>
/// <param name="oControl">The control to check</param>
private void CheckControl(Control oControl)
{
try
{
//Check the control should not be ignored
if (!this.ControlsToIgnore.Contains(oControl.ClientID))
{
//loop recursively
foreach (Control aControl in oControl.Controls)
{
this.CheckControl(aControl);
}
if (oControl is IEditableTextControl)
{
if (this.OriginalValues.ContainsKey(oControl.ClientID))
{
if (this.OriginalValues[oControl.ClientID].ToString() != (oControl as IEditableTextControl).Text)
{
if (!this.ChangedValues.ContainsKey(oControl.ClientID))
{
this.ChangedValues.Add(oControl.ClientID, (oControl as IEditableTextControl).Text);
}
else
{
this.ChangedValues[oControl.ClientID] = (oControl as IEditableTextControl).Text;
}
this.ChangesMade = true;
}
}
}
if (oControl is ICheckBoxControl)
{
if (this.OriginalValues.ContainsKey(oControl.ClientID))
{
if ((bool)this.OriginalValues[oControl.ClientID] != (oControl as ICheckBoxControl).Checked)
{
if (!this.ChangedValues.ContainsKey(oControl.ClientID))
{
this.ChangedValues.Add(oControl.ClientID, (oControl as ICheckBoxControl).Checked);
}
else
{
this.ChangedValues[oControl.ClientID] = (oControl as ICheckBoxControl).Checked;
}
this.ChangesMade = true;
}
}
}
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Stores the default values
/// </summary>
///<param name="oControl">The control to store</param>
public void StoreDefaultValues(Control oControl)
{
try
{
//Reset the Original Values collection
this.OriginalValues = new Dictionary<string, object>();
this.ChangesMade = false;
//Add all relevant controls
this.AddControls(oControl);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Checks the form for changes
/// </summary>
///<param name="oControl">The control to check for changes</param>
public void CheckForChanges(Control oControl)
{
try
{
//Instantiate changed values
this.ChangedValues = new Dictionary<string, object>();
//Reset changes made flag
this.ChangesMade = false;
//Check controls for changes
this.CheckControl(oControl);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Dispose of the object
/// </summary>
public void Dispose()
{
try
{
m_ChangedValues = null;
m_ControlsToIgnore = null;
m_OriginalValues = null;
}
catch (Exception)
{
throw;
}
}
#endregion Methods
}
Base Web Page:
/// <summary>
/// Adds Extra functionality to a web page
/// </summary>
public class WebPage : System.Web.UI.Page
{
#region Properties
/// <summary>
/// Gets or Sets the Change Tracker
/// </summary>
protected ChangeTracker ChangeTracker
{
get
{
if (Session[String.Format("ChangeTracker{0}", this.ClientID)] == null)
{
Session[String.Format("ChangeTracker{0}", this.ClientID)] = new ChangeTracker();
}
return Session[String.Format("ChangeTracker{0}", this.ClientID)] as ChangeTracker;
}
set
{
Session[String.Format("ChangeTracker{0}", this.ClientID)] = value;
}
}
/// <summary>
/// Gets or Sets whether changes have been made
/// </summary>
protected bool ChangesTracked
{
get
{
if (ViewState["ChangesTracked"] == null)
{
ViewState["ChangesTracked"] = false;
}
return (bool)ViewState["ChangesTracked"];
}
set
{
ViewState["ChangesTracked"] = value;
}
}
#endregion Properties
#region Events
/// <summary>
/// Stores the value of the controls after the load completes
/// </summary>
/// <param name="e">order</param>
protected override void OnLoadComplete(EventArgs e)
{
try
{
base.OnLoadComplete(e);
//Dont store on postback
if (!Page.IsPostBack)
{
//Check whether to track changes
if (this.ChangesTracked)
{
//Store the default values
this.ChangeTracker.StoreDefaultValues(this);
}
}
}
catch (Exception)
{
throw;
}
}
#endregion Events
}
Useage (on a save event for example):
//Check for changes
base.ChangeTracker.CheckForChanges(this);
//If changes have been made, flag as dirty
if (base.ChangeTracker.ChangesMade)
{
//Do stuff
}
As you can see the initial values are stored when the page loaded is complete so they are all available for comparison, or in your case, to reset to original values.

Creating Customized biztalk pipeline component to convert HTML to XML?

I am using BizTalk server 2006(R2) and Visual Studio 2005,
In my application i have a html which has to be converted into XML in a customized pipeline component
I am passing a html like this,
<HTML><Customer><Data><Name>Udaya</Name><Age>18</Age><Nation>India</Nation></Data></Customer></HTML>
I must get a out as XML like this
<ns0:Customer xmlns:ns0="http://CWW.com">
<Data>
<Name>Udaya</Name>
<Age>18</Age>
<Nation>India</Nation>
</Data>
</ns0:Customer>
Can anyone give me some suggestion is there any other way to do the same without using customized pipeline?
I am trying with the below pipeline component, but it doesn't work
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.ComponentModel;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
//using System.Windows.Forms;
using System.CodeDom;
using HtmlAgilityPack;
namespace MessageBatchPipelineCompoent
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
[System.Runtime.InteropServices.Guid("6118B8F0-8684-4ba2-87B4-8336D70BD4F7")]
public class DisassemblePipeline : IBaseComponent,
IDisassemblerComponent,
IComponentUI,
IPersistPropertyBag
{
//Used to hold disassembled messages
private System.Collections.Queue qOutputMsgs = new System.Collections.Queue();
private string systemPropertiesNamespace = #"http://schemas.microsoft.com/BizTalk/2003/system-properties";
/// <summary>
/// Batch size used to batch records
/// </summary>
private int _BatchSize;
public int BatchSize
{
get { return _BatchSize; }
set { _BatchSize = value; }
}
/// <summary>
/// Default constructor
/// </summary>
public DisassemblePipeline()
{
}
/// <summary>
/// Description of pipeline
/// </summary>
public string Description
{
get
{
return "Component to convert HTML to XML";
}
}
/// <summary>
/// Name of pipeline
/// </summary>
public string Name
{
get
{
return "HTMLToXMLComponent";
}
}
/// <summary>
/// Pipeline version
/// </summary>
public string Version
{
get
{
return "1.0.0.0";
}
}
/// <summary>
/// Returns collecton of errors
/// </summary>
public System.Collections.IEnumerator Validate(object projectSystem)
{
return null;
}
/// <summary>
/// Returns icon of pipeline
/// </summary>
public System.IntPtr Icon
{
get
{
return new System.IntPtr();
}
}
/// <summary>
/// Class GUID
/// </summary>
public void GetClassID(out Guid classID)
{
classID = new Guid("ACC3F15A-C389-4a5d-8F8E-2A951CDC4C19");
}
/// <summary>
/// InitNew
/// </summary>
public void InitNew()
{
}
/// <summary>
/// Load property from property bag
/// </summary>
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val = null;
try
{
propertyBag.Read("BatchSize", out val, 0);
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val != null)
_BatchSize = (int)val;
else
_BatchSize = 1;
}
/// <summary>
/// Write property to property bag
/// </summary>
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val = (object)BatchSize;
propertyBag.Write("BatchSize", ref val);
}
/// <summary>
/// Disassembles (breaks) message into small messages as per batch size
/// </summary>
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
{
string originalDataString;
try
{
//fetch original message
Stream originalMessageStream = pInMsg.BodyPart.GetOriginalDataStream();
byte[] bufferOriginalMessage = new byte[originalMessageStream.Length];
originalMessageStream.Read(bufferOriginalMessage, 0, Convert.ToInt32(originalMessageStream.Length));
originalDataString = System.Text.ASCIIEncoding.ASCII.GetString(bufferOriginalMessage);
}
catch (Exception ex)
{
throw new ApplicationException("Error in reading original message: " + ex.Message);
}
//XmlDocument originalMessageDoc = new XmlDocument();
//HtmlDocument originalMessageDoc = new HtmlDocument();
HtmlAgilityPack.HtmlDocument originalMessageDoc = new HtmlAgilityPack.HtmlDocument();
originalMessageDoc.Load(originalDataString);
StringBuilder messageString;
try
{
//load original message
// HtmlNode.
// originalMessageDoc.GetElementsByTagName("HTML");
//originalMessageDoc.DocumentElement.FirstChild.RemoveChild(originalMessageDoc.DocumentElement.FirstChild);
// originalMessageDoc.DocumentElement.FirstChild.Attributes.Append("ns0");
// String RootElement = originalMessageDoc.DocumentElement.Name;
messageString = new StringBuilder();
String RootNode = originalMessageDoc.DocumentNode.Name;
System.Diagnostics.EventLog.WriteEntry("Hello", "What doing");
//originalMessageDoc.DocumentNode.Attributes(RootNode,ns0);
//messageString.Insert();
messageString.Append(true);
// messageString.Append("<" + RootNode + " xmlns:ns0='" + "http://SomeContent" + "'>");
//messageString.Append("</" + "Sample" + ">");
CreateOutgoingMessage(pContext, messageString.ToString(), "http://SomeContent", "Sample", pInMsg);
}
catch (Exception ex)
{
throw new ApplicationException("Error in writing outgoing messages: " + ex.Message);
}
finally
{
messageString = null;
originalMessageDoc = null;
}
}
/// <summary>
/// Used to pass output messages`to next stage
/// </summary>
public IBaseMessage GetNext(IPipelineContext pContext)
{
if (qOutputMsgs.Count > 0)
return (IBaseMessage)qOutputMsgs.Dequeue();
else
return null;
}
/// <summary>
/// Queue outgoing messages
/// </summary>
private void CreateOutgoingMessage(IPipelineContext pContext, String messageString, string namespaceURI, string rootElement, IBaseMessage pInMsg)
{
IBaseMessage outMsg;
try
{
//create outgoing message
outMsg = pContext.GetMessageFactory().CreateMessage();
outMsg.Context = pInMsg.Context;
outMsg.AddPart("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
outMsg.Context.Promote("MessageType", systemPropertiesNamespace,"Sample");
// outMsg.Context.Promote("MessageType", systemPropertiesNamespace, "http://SomeText" + "#" + rootElement.Replace("ns0:", ""));
byte[] bufferOoutgoingMessage = System.Text.ASCIIEncoding.ASCII.GetBytes(messageString);
outMsg.BodyPart.Data = new MemoryStream(bufferOoutgoingMessage);
qOutputMsgs.Enqueue(outMsg);
}
catch (Exception ex)
{
throw new ApplicationException("Error in queueing outgoing messages: " + ex.Message);
}
}
}
}
This is the component i have created but this is not working.
Now I am having an error like illegal characters in path.
previously i was using originalMessageDoc.Load(originalDataString);
in the dissassemble function so i was getting the illegal characters in path error ,Now i have solved that particular issue buy replacing it with
originalMessageDoc.LoadHTML(originalDataString);
Now i can see in the event log that i my xml is created but still i have a minor error
when
String RootNode = originalMessageDoc.DocumentNode.Name;
Previously i have given like this now i did hard cored it
now it says rootnode is not declared

How to pass multiple Property Collections to method C#

I am trying to pass a collection of objects to a method in c#.
Here are the methods.
First one expects a single property to be passed.
/// <summary>
/// Adds an EXIF property to an image.
/// </summary>
/// <param name="inputPath">file path of original image</param>
/// <param name="outputPath">file path of modified image</param>
/// <param name="property"></param>
public static void AddExifData(string inputPath, string outputPath, ExifProperty property)
{
using (Image image = Image.FromFile(inputPath))
{
ExifWriter.AddExifData(image, property);
image.Save(outputPath);
}
}
The second one expects a collection of properties. This is the method I want to pass data too.
/// <summary>
/// Adds a collection of EXIF properties to an image.
/// </summary>
/// <param name="inputPath">file path of original image</param>
/// <param name="outputPath">file path of modified image</param>
/// <param name="properties"></param>
public static void AddExifData(string inputPath, string outputPath, ExifPropertyCollection properties)
{
using (Image image = Image.FromFile(inputPath))
{
ExifWriter.AddExifData(image, properties);
image.Save(outputPath);
}
}
To pass data as a single property I use this code.
// Add folder date to exif tag
ExifProperty folderDate = new ExifProperty();
folderDate.Tag = ExifTag.DateTime;
folderDate.Value = lastPart.ToString();
ExifWriter.AddExifData(imagePath, outputPath, copyright);
Here I only pass one property to the method. How could I send multiple items to the method like this.
// add copyright tag
ExifProperty copyright = new ExifProperty();
copyright.Tag = ExifTag.Copyright;
copyright.Value = String.Format(
"Copyright (c){0} Lorem ipsum dolor sit amet. All rights reserved.",
DateTime.Now.Year);
// Add folder date to exif tag
ExifProperty folderDate = new ExifProperty();
folderDate.Tag = ExifTag.DateTime;
folderDate.Value = lastPart.ToString();
Then pass both these properties?
ExifWriter.AddExifData(imagePath, outputPath, ??????????);
Thanks
You're trying to create a params ExifProperty[] parameter.
public static void AddExifData(string inputPath, string outputPath, params ExifProperty[] properties)
{ ... }
ExifWriter.AddExifData(imagePath, outputPath, copyright, folderDate);
Here is the full class without the new code added.
using System;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
namespace ExifUtils.Exif.IO
{
/// <summary>
/// Utility class for writing EXIF data
/// </summary>
public static class ExifWriter
{
#region Fields
private static ConstructorInfo ctorPropertyItem = null;
#endregion Fields
#region Write Methods
/// <summary>
/// Adds a collection of EXIF properties to an image.
/// </summary>
/// <param name="inputPath">file path of original image</param>
/// <param name="outputPath">file path of modified image</param>
/// <param name="properties"></param>
public static void AddExifData(string inputPath, string outputPath, ExifPropertyCollection properties)
{
using (Image image = Image.FromFile(inputPath))
{
ExifWriter.AddExifData(image, properties);
image.Save(outputPath);
}
}
/// <summary>
/// Adds an EXIF property to an image.
/// </summary>
/// <param name="inputPath">file path of original image</param>
/// <param name="outputPath">file path of modified image</param>
/// <param name="property"></param>
public static void AddExifData(string inputPath, string outputPath, ExifProperty property)
{
using (Image image = Image.FromFile(inputPath))
{
ExifWriter.AddExifData(image, property);
image.Save(outputPath);
}
}
/// <summary>
/// Adds a collection of EXIF properties to an image.
/// </summary>
/// <param name="image"></param>
/// <param name="properties"></param>
public static void AddExifData(Image image, ExifPropertyCollection properties)
{
if (image == null)
{
throw new NullReferenceException("image was null");
}
if (properties == null || properties.Count < 1)
{
return;
}
foreach (ExifProperty property in properties)
{
ExifWriter.AddExifData(image, property);
}
}
/// <summary>
/// Adds an EXIF property to an image.
/// </summary>
/// <param name="image"></param>
/// <param name="property"></param>
public static void AddExifData(Image image, ExifProperty property)
{
if (image == null)
{
throw new NullReferenceException("image was null");
}
if (property == null)
{
return;
}
PropertyItem propertyItem;
// The .NET interface for GDI+ does not allow instantiation of the
// PropertyItem class. Therefore one must be stolen off the Image
// and repurposed. GDI+ uses PropertyItem by value so there is no
// side effect when changing the values and reassigning to the image.
if (image.PropertyItems == null || image.PropertyItems.Length < 1)
{
propertyItem = ExifWriter.CreatePropertyItem();
}
else
{
propertyItem = image.PropertyItems[0];
}
propertyItem.Id = (int)property.Tag;
propertyItem.Type = (short)property.Type;
Type dataType = ExifDataTypeAttribute.GetDataType(property.Tag);
switch (property.Type)
{
case ExifType.Ascii:
{
propertyItem.Value = Encoding.ASCII.GetBytes(Convert.ToString(property.Value) + '\0');
break;
}
case ExifType.Byte:
{
if (dataType == typeof(UnicodeEncoding))
{
propertyItem.Value = Encoding.Unicode.GetBytes(Convert.ToString(property.Value) + '\0');
}
else
{
goto default;
}
break;
}
default:
{
throw new NotImplementedException(String.Format("Encoding for EXIF property \"{0}\" has not yet been implemented.", property.DisplayName));
}
}
propertyItem.Len = propertyItem.Value.Length;
// This appears to not be necessary
//foreach (int id in image.PropertyIdList)
//{
// if (id == exif.PropertyItem.Id)
// {
// image.RemovePropertyItem(id);
// break;
// }
//}
image.SetPropertyItem(propertyItem);
}
#endregion Write Methods
#region Copy Methods
/// <summary>
/// Copies EXIF data from one image to another
/// </summary>
/// <param name="source"></param>
/// <param name="dest"></param>
public static void CloneExifData(Image source, Image dest)
{
ExifWriter.CloneExifData(source, dest, -1);
}
/// <summary>
/// Copies EXIF data from one image to another
/// </summary>
/// <param name="source"></param>
/// <param name="dest"></param>
/// <param name="maxPropertyBytes">setting to filter properties</param>
public static void CloneExifData(Image source, Image dest, int maxPropertyBytes)
{
bool filter = (maxPropertyBytes > 0);
// preserve EXIF
foreach (PropertyItem prop in source.PropertyItems)
{
if (filter && prop.Len > maxPropertyBytes)
{
// skip large sections
continue;
}
dest.SetPropertyItem(prop);
}
}
#endregion Copy Methods
#region Utility Methods
/// <summary>
/// Uses Reflection to instantiate a PropertyItem
/// </summary>
/// <returns></returns>
internal static PropertyItem CreatePropertyItem()
{
if (ExifWriter.ctorPropertyItem == null)
{
// Must use Reflection to get access to PropertyItem constructor
ExifWriter.ctorPropertyItem = typeof(PropertyItem).GetConstructor(Type.EmptyTypes);
if (ExifWriter.ctorPropertyItem == null)
{
throw new NotSupportedException("Unable to instantiate a System.Drawing.Imaging.PropertyItem");
}
}
return (PropertyItem)ExifWriter.ctorPropertyItem.Invoke(null);
}
#endregion Utility Methods
}
}
You could use the params keyword:
public static void AddExifData(
string inputPath,
string outputPath,
params ExifProperty[] properties)
{
using (Image image = Image.FromFile(inputPath))
{
ExifWriter.AddExifData(image, new ExifPropertyCollection(properties));
image.Save(outputPath);
}
}
And then call:
ExifWriter.AddExifData(imagePath, outputPath, copyright, folderDate);

Saving Entity Framework objects in ViewState

Given two tables, Make and Model, where a make can contain many models and resulting in the following EF generated entity types...
/// <KeyProperties>
/// ID
/// </KeyProperties>
[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="CarsModel", Name="Make")]
[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
[global::System.Serializable()]
public partial class Make : global::System.Data.Objects.DataClasses.EntityObject
{
/// <summary>
/// Create a new Make object.
/// </summary>
/// <param name="id">Initial value of ID.</param>
public static Make CreateMake(int id)
{
Make make = new Make();
make.ID = id;
return make;
}
/// <summary>
/// There are no comments for Property ID in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public int ID
{
get
{
return this._ID;
}
set
{
this.OnIDChanging(value);
this.ReportPropertyChanging("ID");
this._ID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("ID");
this.OnIDChanged();
}
}
private int _ID;
partial void OnIDChanging(int value);
partial void OnIDChanged();
/// <summary>
/// There are no comments for Property Name in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public string Name
{
get
{
return this._Name;
}
set
{
this.OnNameChanging(value);
this.ReportPropertyChanging("Name");
this._Name = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, true);
this.ReportPropertyChanged("Name");
this.OnNameChanged();
}
}
private string _Name;
partial void OnNameChanging(string value);
partial void OnNameChanged();
/// <summary>
/// There are no comments for Models in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("CarsModel", "FK_Model_Make", "Model")]
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public global::System.Data.Objects.DataClasses.EntityCollection<Model> Models
{
get
{
return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedCollection<Model>("CarsModel.FK_Model_Make", "Model");
}
set
{
if ((value != null))
{
((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedCollection<Model>("CarsModel.FK_Model_Make", "Model", value);
}
}
}
}
/// <KeyProperties>
/// ID
/// </KeyProperties>
[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="CarsModel", Name="Model")]
[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
[global::System.Serializable()]
public partial class Model : global::System.Data.Objects.DataClasses.EntityObject
{
/// <summary>
/// Create a new Model object.
/// </summary>
/// <param name="id">Initial value of ID.</param>
public static Model CreateModel(int id)
{
Model model = new Model();
model.ID = id;
return model;
}
/// <summary>
/// There are no comments for Property ID in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public int ID
{
get
{
return this._ID;
}
set
{
this.OnIDChanging(value);
this.ReportPropertyChanging("ID");
this._ID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("ID");
this.OnIDChanged();
}
}
private int _ID;
partial void OnIDChanging(int value);
partial void OnIDChanged();
/// <summary>
/// There are no comments for Property Name in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public string Name
{
get
{
return this._Name;
}
set
{
this.OnNameChanging(value);
this.ReportPropertyChanging("Name");
this._Name = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, true);
this.ReportPropertyChanged("Name");
this.OnNameChanged();
}
}
private string _Name;
partial void OnNameChanging(string value);
partial void OnNameChanged();
/// <summary>
/// There are no comments for Make in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("CarsModel", "FK_Model_Make", "Make")]
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public Make Make
{
get
{
return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<Make>("CarsModel.FK_Model_Make", "Make").Value;
}
set
{
((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<Make>("CarsModel.FK_Model_Make", "Make").Value = value;
}
}
/// <summary>
/// There are no comments for Make in the schema.
/// </summary>
[global::System.ComponentModel.BrowsableAttribute(false)]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public global::System.Data.Objects.DataClasses.EntityReference<Make> MakeReference
{
get
{
return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<Make>("CarsModel.FK_Model_Make", "Make");
}
set
{
if ((value != null))
{
((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedReference<Make>("CarsModel.FK_Model_Make", "Make", value);
}
}
}
I am trying to store an instance of the Make class into ASP.NET ViewState, along with the associated models as follows...
private Make Make {
get { return this.ViewState[#"EditContext"] as Make; }
set { this.ViewState[#"EditContext"] = value; }
}
and
this.Make = (from make in context.Makes.Include(#"Models")
where make.ID == 1
select make).FirstOrDefault();
But upon PostBack, this.Make.Models is always empty (depite definitely being populated when the entity was placed into the ViewState.
According to the MS Help,
Because entity types support binary
serialization, objects can be saved in
the view state of an ASP.NET
application during a postback
operation. When required, the object
and its related objects are retrieved
from the view state and attached to an
existing object context.
I would therefore expect what I am doing to work. Can anybody explain why this is not the case?
Thanks.
It seems that the problem was my own fault.
After adding the entity to the ViewState, I was manually detaching it from the object context. This was then clearing the links between the Make and the associated Model objects.
By simply adding the object to the ViewState without calling context.Detach(), the Models remain in the list, and I can still call context.Attach() to facilitate the manipulation of the object during the postback.

Resources