Sometimes we want to make a changes to the database or create a backup, but the users must be disconnected for this time. The easiest way is to set the database to the offline state:
ALTER DATABASE dbname SET OffLine WITH ROLLBACK IMMEDIATE
and back
ALTER DATABASE dbname SET OnLine WITH ROLLBACK IMMEDIATE
The alternative is to set the Restrict Access to the Restricted User in the database Properties/Options.
In my previous post I have shown how we can use a C# function in the XSLT. Now I want to go one step further and use in the transformation the ASP.NET controls along with their events.
Let's start from the sample xml data:
<files>
<file Id="F58C2962-AC0D-4C55-80A8-79A724669F53" Name="file 1" Path="D:\Temp\" Extension="iso"/>
<file Id="FDC1358E-D9C8-4A70-ABE0-E0EF5E742E08" Name="file 2" Path="D:\Temp\" Extension="jpg"/>
</files>
I want to create a table from it and for each row I want to have a link which opens the Windows Explorer with this file selected. I have an ASP.NET Development Server so it will work. XSL for this xml looks as follow:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:StringExtensions="urn:StringExtensions" xmlns:asp="remove"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<table>
<tr>
<th>File name</th>
<th>Path</th>
<th>Extension</th>
</tr>
<xsl:for-each select ="files/file">
<tr>
<td>
<xsl:value-of select ="@Name"/>
</td>
<td>
<xsl:value-of select ="@Path"/>
</td>
<td>
<xsl:value-of select ="@Extension"/>
</td>
<td>
<asp:LinkButton runat="server" ID="{StringExtensions:Replace(@Id,'-','')}">Open</asp:LinkButton>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I have used here a custom function to replace some characters. How to create that function is explained in my previous post. Link is in the first line of this post.
We can't just put an ASP.NET control in the stylesheet, because we will get an exception: System.Xml.XmlException: „asp” is not a declared prefix. To workaround it we must put in the stylesheet declaration new attribute: xmlns:asp="remove".
When we transform the xml data with this styleshet, next step is to create an ASP.NET control from html we got:
private Control CreateControl(string html)
{
html.Replace("xmlns:asp=\"remove\"", "");
return Page.ParseControl(html);
}
Now we can add this control to the placeholder control on our page.
The last what we have to do is to bind the click event to ours LinkButton's controls:
private void BindEvents()
{
var r = PlaceHolder1.FlattenChildren().OfType<LinkButton>().ToList();
r.ForEach(x =>
{
x.Click += LinkButton1_Click;
});
}
The FlattenChildren is an extension method which I found somewhere. It returns all the child control:
public static IEnumerable<Control> FlattenChildren(this Control control)
{
var children = control.Controls.Cast<Control>();
return children.SelectMany(c => FlattenChildren(c)).Concat(children);
}
Sample event can look like this:
protected void LinkButton1_Click(object sender, EventArgs e)
{
var elem = ((LinkButton)sender).ID;
Guid fileId = Guid.Parse(elem);
File selectedFile = DBSearchService.GetFileWithPathAndExtension(fileId);
Process.Start(new ProcessStartInfo("explorer", String.Format("/select, {0}\\{1}.{2}",
selectedFile.Path.Path,
selectedFile.Name, selectedFile.Extension.Name)));
}
Using Xslt along with XPath we can transform any xml document in the way we want and use it e.g. to create the html page. At this time, we have Xslt 2.0 and XPath 2, but unfortunatelly .Net Framework doesn't support them. We can only use Xslt 1.
We have two ways to work with Xslt 2. One is to use a 3rd party library, e.g. XQSharp or Saxon. The second option is to manually implement the missed in Xslt 1 functions. How to do it I want to show in this post.
First is the sample code. The goal is to create a table from this xml data:
<?xml version="1.0" encoding="utf-8" ?>
<books>
<book isAvailable="true" averageUsersRating="4.0">
<title>First title</title>
<author>First book author</author>
</book>
<book isAvailable="true" averageUsersRating="1.2">
<title>Second title</title>
<author>Second book author</author>
</book>
<book isAvailable="false" averageUsersRating="5.0">
<title>Third title</title>
<author>Third book author</author>
</book>
</books>
The books which are currently not available must be placed in red table row. And the books titles which have rating greater that 4 must be written in upper case.
Xsl to transform this data to html table looks as follow
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head></head>
<body>
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>Average rating</th>
</tr>
<xsl:for-each select="books/book">
<xsl:choose>
<xsl:when test="@isAvailable='true'">
<tr class="bookIsAvailable">
<xsl:call-template name="bookRowTemplate"/>
</tr>
</xsl:when>
<xsl:otherwise>
<tr class="bookIsNotAvailable">
<xsl:call-template name="bookRowTemplate"/>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="bookRowTemplate" >
<td>
<xsl:choose>
<xsl:when test="@averageUsersRating > 4">
<!-- return title in upper case -->
</xsl:when>
<xsl:otherwise>
<xsl:value-of select ="./title"/>
</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:value-of select ="./author"/>
</td>
<td>
<xsl:value-of select ="@averageUsersRating"/>
</td>
</xsl:template>
</xsl:stylesheet>
This stylesheet is I think self-explanatory. Colouring the appropriate table rows is easy epending on the value of isAvailable attribute.
The second requirement is to write the appropriate books titles in upper case. Xslt/XPath doesn't have (or I don't know) function for that, so we must write it manually.
In C# function this is easy to write:
public class XslStringExtensions
{
public string ConvertToUpperCase(string source)
{
return source.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
}
}
But is it possible to use this C# method in the stylesheet? The answer is yes.
First we must add to the stylesheet new attribute: xmlns:StringExtensions="urn:StringExtensions". Now we can write in the xsl the code for executing this method:
<xsl:when test="@averageUsersRating > 4">
<xsl:value-of select ="StringExtensions:ConvertToUpperCase(./title)"/>
</xsl:when>
At the end, we must configure the XslCompiledTransform to use the custom C# function. To do that, we must create list with arguments:
XsltArgumentList args = new XsltArgumentList();
args.AddExtensionObject("urn:StringExtensions", new XslStringExtensions());'
And that list we must pass to the XslCompiledTransform.Transform object, e.g.
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xslTemplate);
XmlDocument doc=loadedXmlDoc;
MemoryStream returnedHtml = new MemoryStream();
transform.Transform(doc.CreateNavigator(), args, returnedHtml);
NHibernate has many ways to create and executes queries. With one of them* - the Named Queries we can execute our manually created stored procedures. This example is dedicated to SQL Server. This is important to notice, because this code is database specified. That means, for each database the query can look diffrent. E.g. in SQL Server we call the stored procedure by using the syntax: exec <procedure>. In Oracle it is: call <procedure>, so we must remember it.
Let's assume we have a simple stored procedure in our database which takes two arguments:
create procedure FindFilesWithNameLike(@pattern nvarchar(max), @extension nvarchar(10)) as
select f.Id,f.Name,f.[Path],f.Size,f.Extension from [File] f
join Extension e on f.Extension=e.Id
where f.Name
like '%'+@pattern +'%'
and e.Name=@extension
To use it with NHibernate, we must create a mapping. This is done in the *.hbm.xml file (I didn't find if this can be done with Fluent NHibernate and/or mapping by code).
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<sql-query name="FindFilesWithNameLike" callable="true" >
<query-param name="pattern" type="System.String"/>
<query-param name="extension" type="System.String"/>
<return-scalar column ="Id" type ="System.Guid"/>
<return-scalar column ="Name" type ="System.String"/>
<return-scalar column ="Size" type ="System.Int64"/>
<return-scalar column ="Extension" type ="System.String"/>
<return-scalar column ="Path" type ="System.String"/>
exec FindFilesWithNameLike @pattern=:pattern, @extension=:extension
</sql-query>
</hibernate-mapping>
As you can see - nothing special. We are using the query-param to define the procedure parameters and return-scalars to define, what columns (with types) are returned. At the end there is our database-specified code to execute the stored procedure with parameters.
When we have a mapping, we can now execute this query:
public IEnumerable<File> Execute(string pattern, string extension)
{
return session.GetNamedQuery("FindFilesWithNameLike")
.SetParameter("pattern", pattern)
.SetParameter("extension", extension)
.SetResultTransformer(new ResultToFileTransformer())
.List<File>();
}
To do this, we are using the api for named queries. When we execute this query we will have a list of list of objects. Thats why I added the SetResultTransformer. When the database table and custom entity is mapped one to one, we can use the Transformers.AliasToBean<T>, otherwise we can create custom transformer. In this example it is the ResultToFileTransformer. That class must implement the IResultTransformer interface.
*probably with using the native sql support in the NHibernate this it possible either, but I didn't checked
I just wanted to return from sql server data as xml. This is very easy to do, because sql server has a build-in suppot for that.
select Id, Name from Extensions FOR XML RAW('Extension'), ROOT('Extensions')
But using this query, the returned column has name like XML_F52E2B61-18A1-11d1-B105-00805F49916B
It's been some time when I manually created a sql query, so I lost few minutes to think, how to alias this column using the AS keyword.
Correct query looks as follows
select (select Id,Name from Extensions FOR XML RAW('Extension') , ROOT('Extensions')) as alias
I see I just need to remember some basics about sql/tsql.. Too much NHibernate I see.
The WebBrowser control uses behind the scenes the IE engine to render the pages. Fortunatelly/Unfortunatelly is that, we have together with the rendered page the IE-default shortcuts, the context menu shown after pressing the right mouse button etc.
To disable e.g. refreshing the page by pressing F5, we can set the WebBrowserShortcutsEnabled property to false.
To disable the context menu, the WebBrowser control have either a property, called IsWebBrowserContextMenuEnabled. When we set it to false, the context won't be shown after pressing a right mouse button.
At this moment there is only one element to disable. This is when I press the Backspace button. I don't want to have the possibility to go to the previous page by pressing the button on the keybord. Unfortunatelly the control doesn't have a build-in property to disable this behavious, but we can do it by using some javascript code. In the page we can paste this code to do it:
document.onkeydown = function (e)
{
var evt = e || window.event;
if (evt)
{
var keyCode = evt.charCode || evt.keyCode;
if (keyCode === 8)
{
if (evt.preventDefault)
{
evt.preventDefault();
} else
{
evt.returnValue = false;
}
}
}
};
This code will be executed when the page will be loaded.
The WebBrowser control api lets us execute some js code either. If we, e.g. want to disable the backspace button only in certain circumstances, we can make a function from this code and execute it using the api by
webBrowser1.Document.InvokeScript("js function name to execute");
I'm not a desktop application developer. I wrote only one app using the WinForms, but I must tell, it was interesting experience. Now I'm writting second WinForms app and second time, I needed the possibility to updating the progress bar in an app.
So I think this is a good opportunity to put on the blog the code snippet for that to have a place where I can have some useful code for future references.
Ok, so let's go to the solution details. As every desktop application developer know (or should know), the WinForms controls can be accessed/property changed only from the same thread in which they were created. If we have an application and we try to change the progress bar value in some long-running algorithm and all works on the same thread, the program won't be responding to the moment that the algorithm will be over. This is not a situation we want. We want to have a responding gui for e.g. stopping the executing algorithm or simply we want to move the app window to other place on the screen. To have this possibility, the algorithm must be running in other thread and only the gui must be updating from the main thread.
In .NET Framework we have few choices to do that. We have BackgroundWorker class, Dispatcher or we can use Tasks from Task Parallel Library. In my first WinForms app I used the Tasks from TPL to do that so I will focus on this topic in this post.
In the code below is the example
public static File[] Search(SearchCriteria criteria, INotifyProgress notify)
{
notifyHandler = notify;
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
if (notify != null)
{
notify.MinValue = notify.CurrentValue = 0;
notify.MaxValue = criteria.Locations.ToList().Count;
}
notify.MaxValue = 1000;
for (int i = 1; i <= 1000; i++)
{
var task = Task.Factory.StartNew((variables) =>
{
//simulate some long running algorithm
Thread.Sleep(100);
return (int)variables; //return the iteration number
}, i);
task.ContinueWith(x =>
{
notify.CurrentValue = x.Result;
}, scheduler);
}
return null;
}
The for loop is important in this solution. As you can see, for each iteration there is creating a new Task which doing some long-running calculations. When algorithm is finished, the result is returned. Each Task has also a continuation, which is executed after the Task is finished. In this sample, in the continuation there is taken the Task result and this result is set to the progress bar Value property. As I told earlier, the controls can be changed only from the thread where they were created. This is why I used the TaskScheduler.FromCurrentSynchronizationContext() method and set is as a second parameter to the Task continuation. You probably see that, the main Task has a second parameter too. In this parameter we can provide any outer variables which we want to have access to inside the Task. The variables is an alias for our outer variables.
The INotifyProgress interface need one line of explanation too. This is a contract for the class which is a simple wrapper for the WinForms progress bar control. I used it, because I don't want to have reference from the business logic to the gui controls.
public interface INotifyProgress
{
int MaxValue { set; get; }
int MinValue { set; get; }
int CurrentValue { set; get; }
}
public class NotifyProgressBar : INotifyProgress
{
private ProgressBar progressBar;
public NotifyProgressBar(ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public int MaxValue
{
get
{
return progressBar.Maximum;
}
set
{
progressBar.Maximum = value;
}
}
public int MinValue
{
get
{
return progressBar.Minimum;
}
set
{
progressBar.Minimum = value;
}
}
public int CurrentValue
{
get
{
return progressBar.Value;
}
set
{
progressBar.Value = value;
}
}
}
In my previous posts I have shown how to get xml data from database and deserialize it. But sometimes we want to work with the xml and do some operations on it. To working with xml, in the .net framework there are two tools for it. One is to use the XmlDocument class and second is the linq to xml with the XDocument class. I personally prefer to work with linq to xml, so I must convert the XmlDocument object to XDocument which I have after I took the xml from database.
To convert the XmlDocument to XDocument I have following extension methods
public static class XmlDocumentExtensions
{
public static XDocument ToXDocument(this XmlDocument document)
{
return document.ToXDocument(LoadOptions.None);
}
public static XDocument ToXDocument(this XmlDocument document, LoadOptions options)
{
using (XmlNodeReader reader = new XmlNodeReader(document))
{
return XDocument.Load(reader, options);
}
}
}
To convert back, that means from XDocument to XmlDocument I have following extension method
public static XmlDocument ToXmlDocument(this XDocument xDocument)
{
var xmlDocument = new XmlDocument();
using(var xmlReader = xDocument.CreateReader())
{
xmlDocument.Load(xmlReader);
}
return xmlDocument;
}
In one of my previous posts I have used my custom code to serialize and deserialized data to/from XmlDocument class.
Examples of use:
XmlDocument extensions=//some xml document taken from db
List<FileExtension> result =
SerializationUtils.DeSerializeXmlToObject<List<FileExtension>>(extensions);
List<FileExtension> extensions=//some new objects to save to database
XmlDocument serializedExtensions =
SerializationUtils.SerializeObjectToXml<List<FileExtension>>(extensions);
The full code for this snippets:
public static class SerializationUtils
{
public static T DeSerializeXmlToObject<T>(XmlDocument xmlDoc)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringReader reader = new StringReader(xmlDoc.InnerXml);
T result = (T)serializer.Deserialize(reader);
return result;
}
public static XmlDocument SerializeObjectToXml<T>(T obj)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringWriter w = new StringWriter();
serializer.Serialize(w, obj);
XmlDocument result = new XmlDocument();
string xmlContent = w.ToString();
result.LoadXml(xmlContent);
return result;
}
}
In my previous post I have used a OperationsInTransaction.Execute() method. This is my simple wrapper for the NHibernate ISession and ITransaction method.
Normally when we want to execute some query in the transaction we must write this piece of code
using (ISession session = SessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
try
{
//some query
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}
Unfortunatelly we must write it every time when we want to execute some query on the database. Following the DRY principle I have created mini wrapper for it:
public static class OperationsInTransaction
{
private static ISessionFactory sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
sessionFactory = new NhibernateConfig().CreateFactory();
}
return sessionFactory;
}
}
public static void Execute(IsolationLevel level, Action<ISession> operations)
{
using (ISession session = SessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction(level))
{
try
{
operations(session);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
}
public static void Execute(Action<ISession> operations)
{
Execute(IsolationLevel.ReadCommitted, operations);
}
}
In this piece of code I'm creating every time in the background the new ISession and ITransaction objects when I'm execute some queries.
With this wrapper, what I need to write is only:
OperationsInTransaction.Execute(session =>
{
var element = new SetAvailableExtensionsList().Execute(session, serializedExtensions);
session.Save(element);
});