<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7352323701912967109</id><updated>2012-02-16T13:07:58.494-08:00</updated><title type='text'>Mark Cordell's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-638567348290050293</id><published>2011-11-17T00:05:00.001-08:00</published><updated>2011-11-17T00:19:59.051-08:00</updated><title type='text'>Math Problem: Evenly spaced points on a sphere - Part 3</title><content type='html'>&lt;style type="text/css"&gt;
.tbl20111116 { width:400px; }
.tbl20111116 td { width:100px; border-bottom: solid 1px #aaaaaa; }
&lt;/style&gt;
Continued from &lt;a href="/2011/11/math-problem-evenly-spaced-points-on.html"&gt;Part 2&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
Let’s try to find a way to systematically divide the triangles on a sphere into smaller triangles in such a way that the resulting triangles are as evenly spaced as possible, starting with either a tetrahedron, an octahedron, or an icosahedron. One thing we can do is place a new point in the center of each triangle. This is called a Kleetope.  It is not possible to create a distribution of points that is as evenly spaced as possible by producing successive Kleeptopes because the resulting triangles will become less and less like equilateral triangles. Some points will be very close to each other and other points will be further apart.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage001.png" alt="original triangle" style="border:0;" /&gt; &amp;nbsp; 
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage002.png" alt="subdivided once by threes" style="border:0;" /&gt; &amp;nbsp; 
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage003.png" alt="subdivided twice by threes" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
Another thing we can do is split each triangle into four triangles by placing new points at the midpoint of each triangle edge.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage004.png" alt="original triangle" style="border:0;" /&gt; &amp;nbsp; 
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage005.png" alt="subdivided once by fours" style="border:0;" /&gt; &amp;nbsp; 
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage006.png" alt="subdivided twice by fours" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
When you subdivide a triangle into four triangles on the surface of a sphere, the four triangles are not all the same size. The center one is larger. But as you keep subdividing and the triangles get smaller, the curvature of the sphere makes less and less of a difference and the size of the subdivided triangles become more uniform. So if you can show that a set of points after some number of subdivisions are as evenly spaced as possible, then further subdivisions will also be as evenly spaced as possible.
Let’s find the size of the center triangle after subdividing.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage029.png" alt="triangle with labels" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
Use the spherical law of cosines twice, once for the bigger triangle, and once for the smaller triangle. We’ll assume the radius of the sphere is 1.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage012.png" alt="cos(x) = cos(a)^2 + sin(a)^2 cos(t)" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage014.png" alt="cos(2 a) = cos(2 a)^2 + sin(2 a)^2 cos(t)" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
Solve for cos(t).
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage016.png" alt="cos(t) = ( cos(2 a) - cos(2 a)^2 ) / sin(2 a)^2" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
Try to simplify using the pythagorean trigonometric identity and the double angle formula.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage018.png" alt="cos(t) = " style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage020.png" alt="cos(t) = " style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
Substitute for cos(t) in the smaller triangle.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage022.png" alt="cos(x) = " style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage024.png" alt="cos(x) = " style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage026.png" alt="cos(x) = " style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-16-EMFEWMimage028.png" alt="cos(x) = ( 3 cos(a)^2 - 1 ) / ( 2 cos(a)^2 )" style="border:0;" /&gt;
&lt;br /&gt;&lt;br /&gt;
When the big triangle (2 a) is very small, x is barely bigger than a. When 2 a is 2 pi / 9 (or 40&amp;#176;), x is about 5% bigger than a. When 2 a is 4 pi / 9 (or 80&amp;#176;), x is about 24% bigger than a. Finally, when 2 a is 2 pi / 3 (or 120&amp;#176;), x is twice the size of a, which is the same size as the original triangle. This is the case when the three original points are all evenly spaced around the equator, and the three new points are placed on the midpoints, which are also on the equator.
&lt;br /&gt;&lt;br /&gt;
My guess is that you could take a tetrahedron, an octahedron, or an icosahedron, and iteratively split each triangle into four triangles, and you’d end up with an arrangement of points at each iteration that is as evenly spaced as it could be. Additionally, you could form the Kleeptope one time for each of these iterative arrangements and the arrangement of points would be as evenly spaced as they could be.
&lt;br /&gt;&lt;br /&gt;
Iteratively splitting the tetrahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;109.4712&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;54.7356&amp;#176; and 90&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
Forming the Kleetope for each iteration of the tetrahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;109.4712&amp;#176; and something&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;26&lt;/td&gt;&lt;td&gt;72&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;98&lt;/td&gt;&lt;td&gt;288&lt;/td&gt;&lt;td&gt;192&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
Iteratively splitting the octahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;90&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;45&amp;#176; and 60&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;66&lt;/td&gt;&lt;td&gt;192&lt;/td&gt;&lt;td&gt;128&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
Forming the Kleetope for each iteration of the octahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;36&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;90&amp;#176; and something&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;50&lt;/td&gt;&lt;td&gt;144&lt;/td&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;194&lt;/td&gt;&lt;td&gt;576&lt;/td&gt;&lt;td&gt;384&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
Iteratively splitting the icosahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;63.435&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;120&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;td&gt;31.717&amp;#176; and 36&amp;#176;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;162&lt;/td&gt;&lt;td&gt;480&lt;/td&gt;&lt;td&gt;320&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
Forming the Kleetope for each iteration of the icosahedron
&lt;br /&gt;&lt;br /&gt;
&lt;table class="tbl20111116" cellspacing="0"&gt;
&lt;tr&gt;&lt;td&gt;points&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;distance between adjacent points&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;90&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;63.435&amp;#176; and something&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;122&lt;/td&gt;&lt;td&gt;360&lt;/td&gt;&lt;td&gt;240&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;482&lt;/td&gt;&lt;td&gt;1440&lt;/td&gt;&lt;td&gt;960&lt;/td&gt;&lt;td&gt;various&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
So it looks like we can make a lot of arrangements of points that are probably as evenly spaced as possible, and we can make them larger and larger without limit.
&lt;br /&gt;&lt;br /&gt;
However, it may not possible to mathematically specify an arrangement of points that is as evenly spaced as possible for any number of points  n, because when n is a large prime, the optimal arrangement of points is probably unrelated to any patterns of points for any smaller n.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-638567348290050293?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/638567348290050293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=638567348290050293' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/638567348290050293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/638567348290050293'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2011/11/math-problem-evenly-spaced-points-on_17.html' title='Math Problem: Evenly spaced points on a sphere - Part 3'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-4649272048411789687</id><published>2011-11-13T09:13:00.000-08:00</published><updated>2011-11-17T00:21:32.046-08:00</updated><title type='text'>Math Problem: Evenly spaced points on a sphere - Part 2</title><content type='html'>Continued from &lt;a href="/2011/10/math-problem-evenly-spaced-points-on.html"&gt;Part 1&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
In an ideal case, we would be able to position all the points around the sphere so that they formed equilateral triangles and all the adjacent points were exactly the same difference apart.
&lt;br /&gt;&lt;br /&gt;
I should be more specific about what I mean by adjacent points. Draw a line connecting the two points that are closest to each other. Then draw a line connecting the next two points that are the next closest to each other and where the line wouldn’t cross any other existing lines. Continue along like this until no more lines can be drawn. The points with a line connecting them are considered to be adjacent. 
&lt;br /&gt;&lt;br /&gt;
In the ideal case, we can divide the surface area of the sphere by the number of triangles to find the surface area along the sphere of each triangle.
&lt;br /&gt;&lt;br /&gt;
Let’s find the relationship between the number of points, the number of triangle edges, and the number of triangular faces. A tetrahedron has four points, six edges, and four faces.  Whenever you add another point that is not in the same spot as an existing point, it will either fall along an existing edge or on a face. In either case, there will be three more edges and two more faces.
&lt;br /&gt;&lt;br /&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;td style="width:6em"&gt;points&lt;/td&gt;
    &lt;td style="width:8em"&gt;edges&lt;/td&gt;
    &lt;td style="width:8em"&gt;faces&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;4&lt;/td&gt;
    &lt;td&gt;6&lt;/td&gt;
    &lt;td&gt;4&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;5&lt;/td&gt;
    &lt;td&gt;6+3&lt;/td&gt;
    &lt;td&gt;4+2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;6&lt;/td&gt;
    &lt;td&gt;6+3+3&lt;/td&gt;
    &lt;td&gt;4+2+2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;n&lt;/td&gt;
    &lt;td&gt;3 (n - 2)&lt;/td&gt;
    &lt;td&gt;2 (n - 2)&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
So in the ideal case, the spherical surface area A of each triangle is
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage002.png" style="border:0;" alt="A = (2 pi r^2) / (n-2)" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now let’s find the relationship between the distance 'd' along the surface of the sphere between two adjacent points and the spherical surface area of the triangle anchored by these two points. We can use Girard's theorem to find the relationship between the spherical vertex angle 't' and the spherical surface area A, and we can use the spherical law of cosines to find the relationship between the distance 'd' and the vertex angle 't'.
&lt;br /&gt;&lt;br /&gt;
Use the spherical law of cosines.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage004.png" style="border:0;" alt="Let a = d/r" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage006.png" style="border:0;" alt="cos(a) = cos(a)^2 + sin(a)^2 cos(t)" /&gt;
&lt;br /&gt;&lt;br /&gt;
Solve for a.
&lt;br /&gt;&lt;br /&gt;
First use the Pythagorean trigonometric identity to substitute.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage008.png" style="border:0;" alt="cos(a) = cos(a)^2 + (1-cos(a)^2) cos(t)" /&gt;
&lt;br /&gt;&lt;br /&gt;
Group the terms.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage010.png" style="border:0;" alt="(1-cos(t)) cos(a)^2 - cos(a) + cos(t) = 0" /&gt;
&lt;br /&gt;&lt;br /&gt;
Use the quadratic formula.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage012.png" style="border:0;" alt="cos(a) = (1 +- sqrt(1 - 4 (1 - cos(t)) cos(t))) / (2 (1 - cos(t)))" /&gt;
&lt;br /&gt;&lt;br /&gt;
Simplify.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage014.png" style="border:0;" alt="cos(a) = (1 +- sqrt(1 - 4 cos(t) + 4 cos(t)^2)) / (2 (1 - cos(t)))" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage016.png" style="border:0;" alt="cos(a) = (1 +- (1 - 2 cos(t))) / (2 (1 - cos(t)))" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage018.png" style="border:0;" alt="cos(a) = 1 or cos(t) / (1 - cos(t))" /&gt;
&lt;br /&gt;&lt;br /&gt;
We can ignore the case when cos(a) = 1 because that is when a = 0 or 2 π, which is when the two points are in the same location.
&lt;br /&gt;&lt;br /&gt;
Now use Girard's theorem.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage020.png" style="border:0;" alt="A = (3 t - pi) r^2" /&gt;
&lt;br /&gt;&lt;br /&gt;
Solve for t.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage022.png" style="border:0;" alt="t = (A + pi r^2)/(3 r^2)" /&gt;
&lt;br /&gt;&lt;br /&gt;
Substitute for A.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage024.png" style="border:0;" alt="t = (n pi)/(3 (n - 2))" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now substitute this for t in the equation for cos(a) above.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage026.png" style="border:0;" alt="cos(a) = cos((n pi)/(3 (n - 2))) / (1 - cos((n pi)/(3 (n - 2))s))" /&gt;
&lt;br /&gt;&lt;br /&gt;
In most cases we will not be able to position the points into perfect equilateral triangles of this size because the triangles will start to overlap or leave gaps with each other, but we can do it for n=4 the tetrahedron, n=6 the octahedron, and n=12, the icosahedron. 
&lt;br /&gt;&lt;br /&gt;
In the other cases, we can measure the amount of unevenness U by looking at the difference between the ideal distance between adjacent points 'd' and the actual distance between adjacent points dk. Define U as the sum of the squares of these differences:
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://blog.markcordell.s3.amazonaws.com/2011-11-13-jAmTPeimage028.png" style="border:0;" alt="U = sum( (dk - d)^2 )" /&gt;
&lt;br /&gt;&lt;br /&gt;
For the tetrahedron, octahedron, and icosahedron, U = 0. For the other cases, we will try to position the points so that U is as small as possible.
&lt;br /&gt;&lt;br /&gt;
So the next questions are whether there is a way to systematically position the points to minimize U, and given a configuration of points, how can you tell if U for this configuration is the lowest possible.
&lt;br /&gt;&lt;br /&gt;
You can probably show that U is a local minimum if moving any one point by some small distance delta in any direction causes U to increase, but how do you determine whether that local minimum is the lowest minimum possible?
&lt;br /&gt;&lt;br /&gt;
One way to explore this further is to build a computer simulation of marbles around the surface of a sphere that are all magnetically opposed to each other. Or better yet, let the force between any two adjacent points be proportional to the square of the difference between the actual distance between the points and the ideal distance between the points, so that the points are always being pushed towards the ideal distance.
&lt;br /&gt;&lt;br /&gt;
Continue to &lt;a href="/2011/11/math-problem-evenly-spaced-points-on_17.html"&gt;Part 3&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-4649272048411789687?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/4649272048411789687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=4649272048411789687' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/4649272048411789687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/4649272048411789687'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2011/11/math-problem-evenly-spaced-points-on.html' title='Math Problem: Evenly spaced points on a sphere - Part 2'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-1021874350314842769</id><published>2011-10-29T23:17:00.000-07:00</published><updated>2011-11-13T09:21:11.101-08:00</updated><title type='text'>Math Problem: Evenly spaced points on a sphere - Part 1</title><content type='html'>If place points around the circumference of a circle, it is not difficult to position them so that they're evenly spaced.
&lt;br/&gt;&lt;br/&gt;
&lt;img style="border:0; width: 400px; height: 176px;" src="http://3.bp.blogspot.com/-rnhguAXVGCA/TqzC8I4UfbI/AAAAAAAAAGM/uXUbu9bHz7A/s400/regular-polygons.png" border="0" alt="regular polygons" id="BLOGGER_PHOTO_ID_5669120369384258994" /&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;table style="width:280px;"&gt;&lt;thead&gt;&lt;tr style="font-weight:bold;"&gt;&lt;td style="width:25%;"&gt;n&lt;/td&gt;&lt;td&gt;angle&lt;/td&gt;&lt;td&gt;angle&lt;/td&gt;&lt;td&gt;name&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;2π / 3&lt;/td&gt;&lt;td&gt;120°&lt;/td&gt;&lt;td&gt;triangle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;π / 2&lt;/td&gt;&lt;td&gt;90°&lt;/td&gt;&lt;td&gt;square&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;2π / 5&lt;/td&gt;&lt;td&gt;72°&lt;/td&gt;&lt;td&gt;pentagon&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;n&lt;/td&gt;&lt;td&gt;2π / n&lt;/td&gt;&lt;td&gt;360 / n&lt;/td&gt;&lt;td&gt;n-gon&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;br/&gt;&lt;br/&gt;
But suppose you want to position points around the surface of a sphere so that they're evenly spaced.
&lt;br/&gt;&lt;br/&gt;
What is the angle between adjacent points? Is it possible to position the points so that the angle is the same between all adjacent points on the sphere?
&lt;br/&gt;&lt;br/&gt;
Let's look at some examples. For n=4, you have a tetrahedron.
&lt;br/&gt;
&lt;img style="border:0;width: 250px; height: 239px;" src="http://3.bp.blogspot.com/-gTVaoXdlP64/TqzLWePPHwI/AAAAAAAAAGY/gDccSQOiwRU/s400/seq_tetra.jpg" border="0" alt="tetrahedron" id="BLOGGER_PHOTO_ID_5669129617887141634" /&gt;
&lt;br/&gt;&lt;br/&gt;
According to &lt;a href="http://en.wikipedia.org/wiki/Tetrahedron"&gt;wikipedia&lt;/a&gt;, the angle between adjacent points is arccos(-1/3) or about 109.4712°.
&lt;br/&gt;&lt;br/&gt;
For n=6, you have an octahedron. The angle between adjacent points is π / 2 or 90°.
&lt;br/&gt;&lt;br/&gt;
&lt;img style="border:0; width: 169px; height: 186px;" src="http://1.bp.blogspot.com/-zb43QCyEJoQ/TqzSyOCOGEI/AAAAAAAAAGk/mlhWe7Msjeg/s400/octahedron.gif" border="0" alt="octahedron" id="BLOGGER_PHOTO_ID_5669137791155312706" /&gt;
&lt;br/&gt;&lt;br/&gt;
To summarize and expand:
&lt;br/&gt;&lt;br/&gt;
&lt;table style=""&gt;&lt;thead&gt;&lt;tr style="font-weight:bold;"&gt;&lt;td style="width:6%"&gt;n&lt;/td&gt;&lt;td&gt;faces&lt;/td&gt;&lt;td&gt;&lt;span title="triangle faces"&gt;tri. faces&lt;/span&gt;&lt;/td&gt;&lt;td&gt;edges&lt;/td&gt;&lt;td&gt;&lt;span title="triangle edges"&gt;tri. edges&lt;/span&gt;&lt;/td&gt;&lt;td&gt;angle&lt;/td&gt;&lt;td&gt;angle&lt;/td&gt;&lt;td&gt;name&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;arccos(-1/3)&lt;/td&gt;&lt;td&gt;109.4712°&lt;/td&gt;&lt;td&gt;tetrahedron&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;π / 2&lt;/td&gt;&lt;td&gt;90°&lt;/td&gt;&lt;td&gt;octahedron&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td colspan="2" style="text-align:center;"&gt;&lt;span title="there are two angles if you include all triangles"&gt;multiple angles&lt;/span&gt;&lt;/td&gt;&lt;td&gt;cube&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;2 arctan(2/(1+√5))&lt;/td&gt;&lt;td&gt;63.435°&lt;/td&gt;&lt;td&gt;icosahedron&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;n&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt;?&lt;/td&gt;&lt;td&gt; &lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br/&gt;
So what's the answer? I don't know, but one thing we have learned is that a cube's points are not all evenly spaced. The angles between them follow a regular pattern. There are twelve edges with an angle between the points of 2 arcsin(1/√3) &amp;#x2248; 70.528°, and if you include all triangle edges, there are six edges with an angle between the points of 2 arcsin(√2/√3) &amp;#x2248; 109.4712°.
&lt;br/&gt;&lt;br/&gt;
Continue reading &lt;a href="/2011/11/math-problem-evenly-spaced-points-on.html"&gt;Part 2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-1021874350314842769?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/1021874350314842769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=1021874350314842769' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1021874350314842769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1021874350314842769'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2011/10/math-problem-evenly-spaced-points-on.html' title='Math Problem: Evenly spaced points on a sphere - Part 1'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-rnhguAXVGCA/TqzC8I4UfbI/AAAAAAAAAGM/uXUbu9bHz7A/s72-c/regular-polygons.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-1808710691382157257</id><published>2009-07-18T16:57:00.000-07:00</published><updated>2009-07-18T18:58:43.312-07:00</updated><title type='text'>Silverlight freezing when calling XMLHttpRequest?</title><content type='html'>I had a javascript handler for a Silverlight event that modifies the XAML, uses XMLHttpRequest synchronously, then modifies the XAML again, and I noticed that the browser would sometimes not show the first XAML change while the XMLHttpRequest was pending.
&lt;br/&gt;&lt;br/&gt;
Example XAML:
&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt; 
&amp;lt;Canvas xmlns="http://schemas.microsoft.com/client/2007"&amp;gt;
  &amp;lt;TextBlock Name="Txt1" Canvas.Left="25" Canvas.Top="25" FontSize="12" &amp;gt;Click the box&amp;lt;/TextBlock&amp;gt;
  &amp;lt;Rectangle Name="Rect1" Canvas.Left="25" Canvas.Top="40" Width="200" Height="35" Fill="#8080FF" MouseLeftButtonDown="rect1_onMouseDown" /&amp;gt;
&amp;lt;/Canvas&amp;gt;&lt;/pre&gt;
Example javascript:
&lt;pre&gt;function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  var xmlhttp = new XMLHttpRequest();
  xmlhttp.open("GET", "long-running-url.asp", false);
  xmlhttp.send(null);

  rect1.Cursor = "Default";
  txt1.text = "Complete.";
}&lt;/pre&gt;
&lt;br/&gt;
Firefox shows "In Progress..." and then "Complete", but IE waits until everything is finished and then shows "Complete". So when exactly does Silverlight redraw itself when changes are made?
&lt;br/&gt;&lt;br/&gt;
The answer is that changes are not drawn immediately after they are made. They are made the next available chance when no scripts are actively running. 
&lt;br/&gt;&lt;br/&gt;
Example that does not redraw on either Firefox or IE.
&lt;pre&gt;function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  // keep busy for 0.5 sec
  var start = new Date();
  while ((new Date()).getTime() - start.getTime() &amp;lt; 500) {
    // do nothing
  }
  
  rect1.Cursor = "Default";
  txt1.text = "Complete.";
}&lt;/pre&gt;
&lt;br/&gt;
So if you want a redraw while an XMLHttpRequest is pending, do it asynchronously.
&lt;pre&gt;function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  var fnOnStateChange = function() {
    if (xmlhttp.readyState == 4) {
      rect1.Cursor = "Default";
      txt1.text = "Complete.";
    }
  };
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = fnOnStateChange;
  xmlhttp.open("GET", "long-running-url.asp", true);
  xmlhttp.send(null);
}&lt;/pre&gt;
&lt;br/&gt;
Here are a few more examples shown live. The first and third will be redrawn inconsistently and the second one will always redraw during the intermediate "In Progress..." stage. Click the download links below to see the source code for this.
&lt;br/&gt;&lt;br/&gt;
&lt;iframe src="http://blog.markcordell.s3.amazonaws.com/2009-07-18-SynchTest1.htm" width="100%" height="320" frameborder="0"&gt;
&lt;/iframe&gt;
Download &lt;a href="http://blog.markcordell.s3.amazonaws.com/2009-07-18-SynchTest1.htm"&gt;source.html&lt;/a&gt;, &lt;a href="http://blog.markcordell.s3.amazonaws.com/2009-07-18-SynchTest1.xaml"&gt;source.xaml&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-1808710691382157257?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/1808710691382157257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=1808710691382157257' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1808710691382157257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1808710691382157257'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2009/07/silverlight-freezing-when-calling.html' title='Silverlight freezing when calling XMLHttpRequest?'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-1868667869211862028</id><published>2008-12-12T21:08:00.000-08:00</published><updated>2008-12-12T21:19:20.699-08:00</updated><title type='text'>Calling MySQL stored procedures from C# with Connector/Net</title><content type='html'>Have ever got this error when calling a stored procedure?
&lt;pre&gt;
MySql.Data.MySqlClient.MySqlException: SELECT command denied to user 'your_user'@'localhost' for table 'proc'
  at MySql.Data.MySqlClient.MySqlStream.OpenPacket () [0x00000] 
  at MySql.Data.MySqlClient.NativeDriver.ReadResult (System.UInt64&amp; affectedRows, System.Int64&amp; lastInsertId) [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.GetResultSet () [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.NextResult () [0x00000] 
&lt;/pre&gt;
Let me explain what is going on here. The first thing &lt;a href="http://www.mysql.com/products/connector/net/"&gt;Connector/Net&lt;/a&gt; does when you call a stored procedure is select the full text of the stored procedure from the server, parse it, and cache information about the parameters. It then uses that information to build the statement for the actual stored procedure call.
&lt;br/&gt;&lt;br/&gt;
It does this by either calling &lt;br/&gt;
&lt;span class="csharpcode"&gt;SELECT * FROM mysql.proc WHERE db LIKE 'mydb_name' AND name LIKE 'MyProcedure';&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
or by calling&lt;br/&gt;
&lt;span class="csharpcode"&gt;SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA LIKE 'mydb_name' AND ROUTINE_NAME LIKE 'MyProcedure';&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
Why does it do this?
&lt;br/&gt;&lt;br/&gt;
Let me give you a little background. Microsoft SQL Server has a nice feature in that it gives you a lot of flexibility when you call stored procedure. The syntax is&lt;br/&gt;
&lt;span class="csharpcode"&gt;EXEC MyProcedure @MyParam1=123, @MyParam2='blah';&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
The parameters can appear in any order and any of the parameters can be specified as optional in the stored procedure. This is nice when you want to add a new parameter to a stored procedure and release it to your website without taking downtime. First you run an ALTER statement to release the new stored procedure with a new optional parameter, then you release the new application code which passes the new parameter to the stored procedure. Everything works fine when the application code and stored procedure are not in sync because the new parameter is optional.
&lt;br/&gt;&lt;br/&gt;
MySQL's syntax is different. It is&lt;br/&gt;
&lt;span class="csharpcode"&gt;CALL MyProcedure(123, 'blah');&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
The parameters have to appear in the right order and there is no support for optional parameters.
&lt;br/&gt;&lt;br/&gt;
Now let's look at how stored procedures are called from the application code. Calling Microsoft SQL Server from C# code looks like this
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;string&lt;/span&gt; connStr = System.Configuration.&lt;span class="cls"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span class="str"&gt;"MyConnectionString"&lt;/span&gt;].ConnectionString;
&lt;span class="kwrd"&gt;using&lt;/span&gt; (System.Data.SqlClient.&lt;span class="cls"&gt;SqlConnection&lt;/span&gt; conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Data.SqlClient.&lt;span class="cls"&gt;SqlConnection&lt;/span&gt;(connStr)) {
  System.Data.SqlClient.&lt;span class="cls"&gt;SqlCommand&lt;/span&gt; cmd = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Data.SqlClient.&lt;span class="cls"&gt;SqlCommand&lt;/span&gt;(&lt;span class="str"&gt;"MyProcedure"&lt;/span&gt;, conn);
  cmd.CommandType = System.Data.&lt;span class="cls"&gt;CommandType&lt;/span&gt;.StoredProcedure;
  cmd.Parameters.Add(&lt;span class="str"&gt;"@MyParam1"&lt;/span&gt;, System.Data.&lt;span class="cls"&gt;SqlDbType&lt;/span&gt;.Int).Value = 123;
  cmd.Parameters.Add(&lt;span class="str"&gt;"@MyParam2"&lt;/span&gt;, System.Data.&lt;span class="cls"&gt;SqlDbType&lt;/span&gt;.VarChar, 40).Value = &lt;span class="str"&gt;"blah"&lt;/span&gt;;
  conn.Open();
  cmd.ExecuteNonQuery();
}
&lt;/pre&gt;
MySQL Connector/Net mimics this syntax fairly closely.
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;string&lt;/span&gt; connStr = System.Configuration.&lt;span class="cls"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span class="str"&gt;"MyConnectionString"&lt;/span&gt;].ConnectionString;
&lt;span class="kwrd"&gt;using&lt;/span&gt; (MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt; conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt;(connStr)) {
  MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt; cmd = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt;(&lt;span class="str"&gt;"MyProcedure"&lt;/span&gt;, conn);
  cmd.CommandType = System.Data.&lt;span class="cls"&gt;CommandType&lt;/span&gt;.StoredProcedure;
  cmd.Parameters.Add(&lt;span class="str"&gt;"@MyParam1"&lt;/span&gt;, MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlDbType&lt;/span&gt;.Int32).Value = 123;
  cmd.Parameters.Add(&lt;span class="str"&gt;"@MyParam2"&lt;/span&gt;, MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlDbType&lt;/span&gt;.String).Value = &lt;span class="str"&gt;"blah"&lt;/span&gt;;
  conn.Open();
  cmd.ExecuteNonQuery();
}
&lt;/pre&gt;
So in order for Connector/Net to figure out what order to put the parameters when it creates the CALL statement it gets the stored procedure definition from the server and looks at the order that the parameters appear in the original CREATE PROCEDURE statement.
&lt;br/&gt;&lt;br/&gt;
There are a couple of problems with this.
&lt;ol&gt;
&lt;li&gt;The account the application code is running under may not have select permissions on mysql.proc and if you are using shared web hosting &lt;a href="/2008/11/mysql-stored-procedure-permissions-and.html"&gt;it may not be possible to give your account these permissions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;There is a performance penalty for retrieving and storing the stored procedure bodies.&lt;/li&gt;
&lt;/ol&gt;
So what I recommend doing is building the CALL statement yourself as a parameterized query.
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;My recommended way to call MySQL stored procedures&lt;/b&gt;
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;string&lt;/span&gt; connStr = System.Configuration.&lt;span class="cls"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span class="str"&gt;"MyConnectionString"&lt;/span&gt;].ConnectionString;
&lt;span class="kwrd"&gt;using&lt;/span&gt; (MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt; conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt;(connStr)) {
  MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt; cmd = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt;();
  cmd.Connection = conn;
  cmd.CommandText = &lt;span class="str"&gt;"CALL MyProcedure(@MyParam1, @MyParam2);"&lt;/span&gt;;
  cmd.Parameters.AddWithValue(&lt;span class="str"&gt;"@MyParam1"&lt;/span&gt;, 123);
  cmd.Parameters.AddWithValue(&lt;span class="str"&gt;"@MyParam2"&lt;/span&gt;, &lt;span class="str"&gt;"blah"&lt;/span&gt;);
  &lt;span class="rem"&gt;// Or if you prefer this style:&lt;/span&gt;
  &lt;span class="rem"&gt;//cmd.Parameters.Add("@MyParam1", MySql.Data.MySqlClient.MySqlDbType.Int32).Value = 123;&lt;/span&gt;
  &lt;span class="rem"&gt;//cmd.Parameters.Add("@MyParam2", MySql.Data.MySqlClient.MySqlDbType.String).Value = "blah";&lt;/span&gt;
  conn.Open();
  cmd.ExecuteNonQuery();
}
&lt;/pre&gt;
Connector/Net will go through the CommandText and replace @MyParam1 and @MyParam2 with the values you provide so the statement that gets sent to the MySQL server is just &lt;span class="csharpcode"&gt;CALL MyProcedure(123, 'blah');&lt;/span&gt; It's a good idea to do it with parameters because the library code will properly escape single quote characters. If you don't do this, the SQL statement could end up with syntax errors, or worse, end users might be able to run malicious SQL queries against your MySQL server.
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Output parameters&lt;/b&gt;
&lt;br/&gt;&lt;br/&gt;
Connector/Net does not handle parameters with &lt;span class="csharpcode"&gt;Direction = System.Data.&lt;span class="cls"&gt;ParameterDirection&lt;/span&gt;.Output&lt;/span&gt; unless &lt;span class="csharpcode"&gt;CommandType = System.Data.&lt;span class="cls"&gt;CommandType&lt;/span&gt;.StoredProcedure&lt;/span&gt;. In our case we're using &lt;span class="csharpcode"&gt;&lt;span class="cls"&gt;CommandType&lt;/span&gt;.Text&lt;/span&gt; so we can only use &lt;span class="csharpcode"&gt;&lt;span class="cls"&gt;ParameterDirection&lt;/span&gt;.Input&lt;/span&gt;.
&lt;br/&gt;&lt;br/&gt;
So what do we do if we want to call a stored procedure that has an output parameter?
&lt;br/&gt;&lt;br/&gt;
We use &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/user-variables.html"&gt;MySQL User Variables&lt;/a&gt;. This is the statement we will send to the MySQL server.&lt;br /&gt;
&lt;span class="csharpcode"&gt;CALL MyProcedure(@MyOutputParam1, @MyOutputParam2, 123, 'blah');&lt;br /&gt;
SELECT @MyOutputParam1, @MyOutputParam2;
&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
But before we can make this work there are a couple things we need to take care of. One is that we need to include &lt;span class="csharpcode"&gt;Allow User Variables=true;&lt;/span&gt; in the connection string.
&lt;br/&gt;&lt;br/&gt;
&lt;span class="csharpcode"&gt;Server=localhost; Port=3306; Database=your_database; Uid=your_user; Pwd=your_password; Connect Timeout=30; &lt;b&gt;Allow User Variables=true;&lt;/b&gt;&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
If we don't do this Connector/Net will throw an exception. This is because it checks that the parameter names in the CommandText match up with the parameters that have been added to the list and it doesn't have a way to distinguish between user variables and parameters. If we set &lt;span class="csharpcode"&gt;Allow User Variables=true;&lt;/span&gt; it will skip this check.
&lt;br/&gt;&lt;br/&gt;
The other thing we have to take care of is the data type of the user variables. Connector/Net does not handle result sets with user variables in them properly.
&lt;br/&gt;&lt;br/&gt;
To see for yourself, try running this query:
&lt;pre&gt;SET @a = true, @b = 222, @c = -1, @d = 'abcd';
SELECT @a, @b, @c, @d, true, 222, -1, 'abcd';
&lt;/pre&gt;
Connector/Net returns the first three columns in the result set as byte[] and sets the contents of the byte array to be the raw bytes sent back by the MySQL server. The fourth column is a normal string and the rest of the columns are all handled properly.
&lt;br/&gt;&lt;br/&gt;
The MySQL Command Line Client, on the other hand, handles user variables properly. You can see it here.
&lt;pre&gt;
mysql&gt; SET @a = true, @b = 222, @c = -1, @d = 'abcd';
Query OK, 0 rows affected (0.00 sec)

mysql&gt; SELECT @a, @b, @c, @d, true, 222, -1, 'abcd';
+------+------+------+------+------+-----+----+------+
| @a   | @b   | @c   | @d   | TRUE | 222 | -1 | abcd |
+------+------+------+------+------+-----+----+------+
| 1    | 222  | -1   | abcd |    1 | 222 | -1 | abcd |
+------+------+------+------+------+-----+----+------+
1 row in set (0.00 sec)
&lt;/pre&gt;
I was curious what was going on so I intercepted the response sent back by the MySQL server to see if I could tell why Connector/Net was failing. It sends the column definitions first and then the contents of each row. I could see that the row data was the same for column @a and column TRUE, column @b and column 222, and so on, but the header containing the column definitions were different. I didn't investigate further and instead looked for a workaround.
&lt;!-- 
you can see that nothing MySQL returns the user variables in the same format as the direct values.

2d 00 00 00  03 73 65 6c  65 63 74 20  40 61 2c 20  40 62 2c 20  40 63 2c 20  40 64 2c 20  74 72 75 65   -....select @a, @b, @c, @d, true
2c 20 32 32  32 2c 20 2d  31 2c 20 27  61 62 63 64  27                                                   , 222, -1, 'abcd'

01 00 00 01  08 18 00 00  02 03 64 65  66 00 00 00  02 40 61 00  0c 3f 00 14  00 00 00 fd  80 00 00 00   ..........def....@a..?.....y....
00 18 00 00  03 03 64 65  66 00 00 00  02 40 62 00  0c 3f 00 14  00 00 00 fd  80 00 00 00  00 18 00 00   ......def....@b..?.....y........
04 03 64 65  66 00 00 00  02 40 63 00  0c 3f 00 14  00 00 00 fd  80 00 00 00  00 18 00 00  05 03 64 65   ..def....@c..?.....y..........de
66 00 00 00  02 40 64 00  0c 08 00 00  20 00 00 fd  00 00 1f 00  00 1a 00 00  06 03 64 65  66 00 00 00   f....@d..... ..y..........def...
04 54 52 55  45 00 0c 3f  00 01 00 00  00 08 81 00  00 00 00 19  00 00 07 03  64 65 66 00  00 00 03 32   .TRUE..?................def....2
32 32 00 0c  3f 00 03 00  00 00 08 81  00 00 00 00  18 00 00 08  03 64 65 66  00 00 00 02  2d 31 00 0c   22..?................def....-1..
3f 00 02 00  00 00 08 81  00 00 00 00  1a 00 00 09  03 64 65 66  00 00 00 04  61 62 63 64  00 0c 08 00   ?................def....abcd....
04 00 00 00  fd 01 00 1f  00 00 05 00  00 0a fe 00  00 02 00 1c  00 00 0b 01  31 03 32 32  32 02 2d 31   ....y........._.........1.222.-1
04 61 62 63  64 01 31 03  32 32 32 02  2d 31 04 61  62 63 64 05  00 00 0c fe  00 00 02 00                .abcd.1.222.-1.abcd...._....

def....@a
64 65 66 00 00 00 02 40 61             00 0c 3f 00 14 00 00 00 fd 80 00 00 00 00 18 00 00 03 03

def....TRUE
64 65 66 00 00 00 04 54 52 55 45       00 0c 3f 00 01 00 00 00 08 81 00 00 00 00 19 00 00 07 03
--&gt;
&lt;br/&gt;&lt;br/&gt;
Here is what I did to make it work.
&lt;br/&gt;&lt;br/&gt;
&lt;span class="csharpcode"&gt;CALL MyProcedure(@MyOutputNum1, @MyOutputString2, 123, 'blah');&lt;br /&gt;
SELECT CAST(@MyOutputNum1 AS SIGNED), @MyOutputString2;
&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
If your parameters are not signed integers you will need to cast them as something else. &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html"&gt;Read about using CAST and CONVERT&lt;/a&gt;. Note that SIGNED and UNSIGNED are both 64 bit integers.
&lt;br/&gt;&lt;br/&gt;
Now that I know what to do I can create the application code.
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;string&lt;/span&gt; connStr = System.Configuration.&lt;span class="cls"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span class="str"&gt;"MyConnectionString"&lt;/span&gt;].ConnectionString;
&lt;span class="kwrd"&gt;using&lt;/span&gt; (MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt; conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt;(connStr)) {
  MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt; cmd = &lt;span class="kwrd"&gt;new&lt;/span&gt; MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlCommand&lt;/span&gt;();
  cmd.Connection = conn;
  cmd.CommandText = &lt;span class="str"&gt;"CALL MyProcedure(@MyOutputNum1, @MyOutputString2, ?MyParam1, ?MyParam2); SELECT CAST(@MyOutputNum1 AS SIGNED), @MyOutputString2;"&lt;/span&gt;;
  &lt;span class="rem"&gt;// I am using the ?param style to make it easy to differentiate between user variables and parameters. @MyParam1 would also work.&lt;/span&gt;
  cmd.Parameters.AddWithValue(&lt;span class="str"&gt;"?MyParam1"&lt;/span&gt;, 123);
  cmd.Parameters.AddWithValue(&lt;span class="str"&gt;"?MyParam2"&lt;/span&gt;, &lt;span class="str"&gt;"blah"&lt;/span&gt;);
  conn.Open();
  MySql.Data.MySqlClient.&lt;span class="cls"&gt;MySqlDataReader&lt;/span&gt; rdr = cmd.ExecuteReader();
  &lt;span class="rem"&gt;// If MyProcedure returns a result set that will come first so you will need: &lt;/span&gt;
  &lt;span class="rem"&gt;//   while (rdr.Read()) {...} &lt;/span&gt;
  &lt;span class="rem"&gt;//   and rdr.NextResult();&lt;/span&gt;
  &lt;span class="rem"&gt;// Now get the output parameters.&lt;/span&gt;
  &lt;span class="kwrd"&gt;long&lt;/span&gt; myOutputNum1 = -1;
  &lt;span class="kwrd"&gt;string&lt;/span&gt; myOutputString2 = &lt;span class="kwrd"&gt;null&lt;/span&gt;;
  &lt;span class="kwrd"&gt;if&lt;/span&gt; (rdr.Read()) {
    &lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0;
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (rdr.FieldCount &amp;gt; i &amp;amp;&amp;amp; !rdr.IsDBNull(i)) myOutputNum1 = rdr.GetInt64(i);
    i++;
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (rdr.FieldCount &amp;gt; i &amp;amp;&amp;amp; !rdr.IsDBNull(i)) myOutputString2 = rdr.GetString(i);
  }
}
&lt;/pre&gt;
That worked.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-1868667869211862028?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/1868667869211862028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=1868667869211862028' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1868667869211862028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1868667869211862028'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/12/calling-mysql-stored-procedures-from-c.html' title='Calling MySQL stored procedures from C# with Connector/Net'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-5768192158734296562</id><published>2008-12-06T10:50:00.000-08:00</published><updated>2008-12-06T11:21:54.155-08:00</updated><title type='text'>Javascript and C# encryption</title><content type='html'>If you are unable to use SSL and you want to use encryption, one option is to use javascript to encrypt and send your data. Then on the server side you can decrypt the data, process it, and send back an encrypted response to be decrypted by javascript.
&lt;br/&gt;&lt;br/&gt;
I have created a page which does this. I used AES (also known as Rijndael) for the private key encryption algorithm. The .NET framework has a built-in class library implementation of this called &lt;span class="csharpcode"&gt;System.Security.Cryptography.&lt;span class="cls"&gt;RijndaelManaged&lt;/span&gt;&lt;/span&gt; and there are some javascript implementations out there on the web. I copied one of them and made some modifications to it, such as adding Base64 encoding.
&lt;br/&gt;&lt;br/&gt;
The private key is hard coded in the server side code and must be entered into a textbox on the page by the user.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-06-EncryptMaster.zip"&gt;Download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-5768192158734296562?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/5768192158734296562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=5768192158734296562' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/5768192158734296562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/5768192158734296562'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/12/javascript-and-c-encryption.html' title='Javascript and C# encryption'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-3206559243447355607</id><published>2008-12-06T09:53:00.000-08:00</published><updated>2008-12-12T21:20:05.857-08:00</updated><title type='text'>Running T-SQL queries from your browser against Microsoft SQL Server</title><content type='html'>In a previous blog post I wrote about &lt;a href="http://markcordell.blogspot.com/2008/12/running-mysql-queries-from-your-browser.html"&gt;how to run MySQL queries from your browser&lt;/a&gt;. I thought I would post the source code for running T-SQL queries against Microsoft SQL Server as well, in case anyone wants it.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-06-MSSqlQuery.aspx"&gt;Download source code&lt;/a&gt; 
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-06-EncryptSql.zip"&gt;I also made a version that encrypts data before transmitting it&lt;/a&gt;.
&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-3206559243447355607?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/3206559243447355607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=3206559243447355607' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/3206559243447355607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/3206559243447355607'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/12/running-t-sql-queries-from-your-browser.html' title='Running T-SQL queries from your browser against Microsoft SQL Server'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-2750270385977513477</id><published>2008-12-05T20:25:00.000-08:00</published><updated>2008-12-12T21:21:10.889-08:00</updated><title type='text'>Running MySQL queries from your browser</title><content type='html'>&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-05-MySqlQuery.aspx"&gt;Download the code&lt;/a&gt;. You will also need to download the &lt;a href="http://dev.mysql.com/downloads/connector/net/"&gt;Connector/Net binaries&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
phpMyAdmin has a place for you to run MySQL queries but there are a few reasons you might want to have a your own admin page on your site for running ad hoc queries.
&lt;ol&gt;
&lt;li&gt;You might want your queries to run under a different user than the one used by phpMyAdmin. See &lt;a href="http://markcordell.blogspot.com/2008/11/mysql-stored-procedure-permissions-and.html"&gt;here&lt;/a&gt; for why you may need to run your stored procedure creation scripts this way.&lt;/li&gt;
&lt;li&gt;You might want to test a query going through Connector/Net.&lt;/li&gt;
&lt;li&gt;Maybe you just want a way to run queries faster without having to log in.&lt;/li&gt;
&lt;/ol&gt;
Here is a snippet from a simple page I created to run MySQL queries against my site.
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; btnSubmit_OnClick(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, System.&lt;span class="cls"&gt;EventArgs&lt;/span&gt; e) {
  &lt;span class="kwrd"&gt;if&lt;/span&gt; (txtKey.Text == &lt;span class="str"&gt;"some random string"&lt;/span&gt;) {
    &lt;span class="cls"&gt;StringBuilder&lt;/span&gt; sb = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="cls"&gt;StringBuilder&lt;/span&gt;();
    &lt;span class="kwrd"&gt;using&lt;/span&gt; (&lt;span class="cls"&gt;MySqlConnection&lt;/span&gt; conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="cls"&gt;MySqlConnection&lt;/span&gt;(&lt;span class="str"&gt;"Server=localhost; Port=3306; ..."&lt;/span&gt;)) {
      &lt;span class="cls"&gt;MySqlCommand&lt;/span&gt; cmd = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="cls"&gt;MySqlCommand&lt;/span&gt;(txtSql.Text, conn);
      conn.Open();
      &lt;span class="cls"&gt;MySqlDataReader&lt;/span&gt; rdr = cmd.ExecuteReader();
      &lt;span class="kwrd"&gt;while&lt;/span&gt; (...) {
        ...
        sb.Append(results of query);
        ...
      }
    }
    litOutput.Text = sb.ToString();
  }
}&lt;/pre&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-05-MySqlQuery.aspx"&gt;Download the real code&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Security considerations&lt;/b&gt;
&lt;ol&gt;
&lt;li&gt;Remember to put in a random string for your key in the page.&lt;/li&gt;
&lt;li&gt;You may want to remove or disable the page when you are not using it.&lt;/li&gt;
&lt;li&gt;Use ssl encryption whenever you visit the page. If you don't have an ssl certificate for your site you can create a self signed certificate in cPanel if your hosting provider lets you. Mine lets me create and import certificates but they don't let me turn on ssl for my site unless I pay for an upgrade so I cannot actually do anything with them. A self signed certificate will give you a warning in your browser but the transmission will still be encrypted. If you are like me and can't enable ssl for your site another option is to &lt;a href="/2008/12/javascript-and-c-encryption.html"&gt;encrypt the data in javascrypt before sending it to your server side code&lt;/a&gt;.&lt;br/&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-12-06-EncryptSql.zip"&gt;Download source code of a version that uses encryption&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-2750270385977513477?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/2750270385977513477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=2750270385977513477' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/2750270385977513477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/2750270385977513477'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/12/running-mysql-queries-from-your-browser.html' title='Running MySQL queries from your browser'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-5369033902500904282</id><published>2008-11-26T19:31:00.000-08:00</published><updated>2008-12-12T21:22:23.398-08:00</updated><title type='text'>MySQL stored procedure permissions and cPanel</title><content type='html'>To set up my site I created all my MySQL tables and stored procedures by running a script in phpMyAdmin, and then I deployed my application files. This resulted in the following error:
&lt;pre&gt;execute command denied to user 'my_user'@'localhost' for routine 'my_database.MyProc'
Description: HTTP 500. Error processing request.

Stack Trace: 

MySql.Data.MySqlClient.MySqlException: execute command denied to user 'my_user'@'localhost' for routine 'my_database.MyProc'
  at MySql.Data.MySqlClient.MySqlStream.OpenPacket () [0x00000] 
  at MySql.Data.MySqlClient.NativeDriver.ReadResult (System.UInt64&amp; affectedRows, System.Int64&amp; lastInsertId) [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.GetResultSet () [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.NextResult () [0x00000]&lt;/pre&gt;
The problem is that cPanel only allows you to grant permissions for a subset of actions.
&lt;pre&gt;&amp;nbsp;SELECT                       CREATE
 INSERT                       ALTER
 UPDATE                       DROP
 DELETE                       LOCK TABLES
 INDEX                        REFERENCES
 CREATE TEMPORARY TABLES      CREATE ROUTINE&lt;/pre&gt;
 The key one that they inexplicably did not add is EXECUTE.
&lt;br /&gt;&lt;br /&gt;
CREATE ROUTINE, ALTER ROUTINE, and EXECUTE were all added to MySQL in version 5.0.3 and have been available since March 2005. I am using shared web hosting that was set up with the latest version of MySQL (version 5.0.67, which came out in August 2008) and the latest version of cPanel (version 11 with the latest build from November 2008). So if I do a little math in my head I can see that sometime in the last three and a half years, the people who make cPanel became aware that MySQL added support for stored procedures and realized that they needed to add a way for people to grant MySQL users the CREATE ROUTINE permission but they still haven't done anything about the EXECUTE permission.
&lt;br /&gt;&lt;br /&gt;
It is not an ideal situation.
&lt;br /&gt;&lt;br /&gt;
What I would like is to have an admin user that has full permissions to do everything on a particular database -- including make schema changes and create stored procedures -- and another user that only has permission to SELECT from tables and EXECUTE routines. The user with the limited permissions is the one that I put in my web.config and use from my web application code.
&lt;br /&gt;&lt;br /&gt;
So I tried granting the permissions by manually executing the command in phpMyAdmin.
&lt;pre&gt;GRANT EXECUTE ON my_database.* TO my_user@localhost;&lt;/pre&gt;
It didn't work.
&lt;pre&gt;#1044 - Access denied for user 'my_admin_user'@'localhost' to database 'my_database'&lt;/pre&gt;
This is another thing that cPanel has not done correctly. My admin user should have been set up with the GRANT permission on my_database. Now there is no way for me to give a user EXECUTE permission. It won't even work if I try using the DEFINER syntax in MySQL.
&lt;pre&gt;DELIMITER $$

DROP PROCEDURE IF EXISTS MyProc$$

CREATE DEFINER = my_user@localhost PROCEDURE MyProc()
BEGIN

-- &amp;lt;procedure body&amp;gt;

END $$

DELIMITER ;&lt;/pre&gt;
I get an error.
&lt;pre&gt;#1227 - Access denied; you need the SUPER privilege for this operation &lt;/pre&gt;
So what do I do?
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;The Solution&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
Create the stored procedures using the account with limited permissions. MySQL keeps track of which account was used to create each stored procedure and automatically allows that account permission to execute it.
&lt;br /&gt;&lt;br /&gt;
Here is a step by step guide to the process.
&lt;ol&gt;
&lt;li&gt;Go into cPanel and check the box for CREATE ROUTINE for your my_user account.&lt;/li&gt;
&lt;li&gt;Upload a script to your website that will create your stored procedures using my_user. &lt;br /&gt;&lt;a href="http://markcordell.blogspot.com/2008/12/running-mysql-queries-from-your-browser.html"&gt;Here&lt;/a&gt; is a script that you can use.&lt;/li&gt;
&lt;li&gt;Call your script from your browser.&lt;/li&gt;
&lt;li&gt;Remove the script from your website.&lt;/li&gt;
&lt;li&gt;Go into cPanel and uncheck the box for CREATE ROUTINE.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-5369033902500904282?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/5369033902500904282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=5369033902500904282' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/5369033902500904282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/5369033902500904282'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/mysql-stored-procedure-permissions-and.html' title='MySQL stored procedure permissions and cPanel'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-4177117538052785416</id><published>2008-11-22T19:33:00.000-08:00</published><updated>2008-11-26T17:12:41.425-08:00</updated><title type='text'>Xml Deserialization: a home grown method</title><content type='html'>&lt;a href="https://sites.google.com/site/markcordellblog/Home/2008-11-22-Util.cs?attredirects=0" style="display:none;"&gt;Download source code&lt;/a&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-11-22-Util.cs"&gt;Download source code&lt;/a&gt;
&lt;br/&gt;
If you have ever called &lt;span class="csharpcode"&gt;System.Xml.Serialization.&lt;span class="cls"&gt;XmlSerializer&lt;/span&gt;.Deserialize&lt;/span&gt; and got this exception: &lt;span class="csharpcode"&gt;System.InvalidOperationException: '' was not expected&lt;/span&gt;, you know that getting the xml namespaces right for deserialization can sometimes be a trial and error process.
&lt;br/&gt;&lt;br/&gt;
If you have access to the class you want to deserialize to you can add a custom attribute to define the xml namespace to use for serialization.
&lt;pre class="csharpcode"&gt;[System.Xml.Serialization.&lt;span class="cls"&gt;XmlTypeAttribute&lt;/span&gt;(Namespace=&lt;span class="str"&gt;"MyNamespace"&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="cls"&gt;MyClass&lt;/span&gt; {
    ...
}&lt;/pre&gt;
If the class is in one of the built in libraries or in a third party library and you can't modify it you can modify the xml to add a namespace attribute instead.
&lt;pre&gt;&amp;lt;MyClass xmlns="MyNamespace"&amp;gt;&lt;/pre&gt;
Another alternative is to use different deserialization code that is more forgiving about things like xml namespaces and case sensitivity. I have created one that uses the XML DOM parser System.Xml.XmlDocument and System.Reflection to iterate through the public fields and properties of an object and find the xml elements and attributes that best match the field names.
&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// simplified snippet&lt;/span&gt;
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; T XmlStrToObjBestWeCan&amp;lt;T&amp;gt;(&lt;span class="kwrd"&gt;string&lt;/span&gt; xml) {
    System.Xml.&lt;span class="cls"&gt;XmlDocument&lt;/span&gt; doc = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Xml.&lt;span class="cls"&gt;XmlDocument&lt;/span&gt;();
    doc.LoadXml(xml);
    &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (System.Reflection.&lt;span class="cls"&gt;FieldInfo&lt;/span&gt; field &lt;span class="kwrd"&gt;in&lt;/span&gt; &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(T).GetFields()) {
        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;object&lt;/span&gt; attribute &lt;span class="kwrd"&gt;in&lt;/span&gt; field.GetCustomAttributes(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(System.Xml.Serialization.&lt;span class="cls"&gt;XmlElementAttribute&lt;/span&gt;), &lt;span class="kwrd"&gt;false&lt;/span&gt;)) {
            ...
        }
        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (System.Xml.XmlNode child &lt;span class="kwrd"&gt;in&lt;/span&gt; doc.DocumentElement.ChildNodes) {
            ...
        }
&lt;/pre&gt;
&lt;a href="http://blog.markcordell.s3.amazonaws.com/2008-11-22-Util.cs"&gt;Download complete file&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
You can download the code and include it in your project to see how it compares to the built in deserializer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-4177117538052785416?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/4177117538052785416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=4177117538052785416' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/4177117538052785416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/4177117538052785416'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/xml-deserialization-home-grown-method.html' title='Xml Deserialization: a home grown method'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-714974649883467583</id><published>2008-11-20T21:00:00.000-08:00</published><updated>2008-11-24T00:25:47.575-08:00</updated><title type='text'>ASP.NET 1.1 and 2.0 on Mono</title><content type='html'>One of the first problems I had when setting up my ASP.NET site on Linux and Mono was that the server I was using was configured to use ASP.NET 1.1 instead of 2.0. This showed up as an error in my Page directive about the Title attribute.
&lt;br /&gt;&lt;br /&gt;
&lt;div style="font-family:Verdana;font-weight:normal;font-size:10pt;color:black;border:solid 1px #808080;padding:4px;"&gt;&lt;span style="font-size:14pt;color:maroon;"&gt;&lt;i&gt;Parser Error&lt;/i&gt;&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Description: &lt;/b&gt;Error parsing a resource required to service this request. Review your source file and modify it to fix this error.
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Parser Error Message: &lt;/b&gt;Unknown attribute: Title
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Source Error:&lt;/b&gt;
&lt;div style="font-family:Lucida Console;font-size:9pt;background:#ffffaa;"&gt; 
&lt;span style="color:red;"&gt;Line 1: &amp;lt;%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Untitled Page" %&amp;gt; &lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br /&gt;
So I removed the Title attribute and I got new error.
&lt;br /&gt;&lt;br /&gt;
&lt;div style="font-family:Verdana;font-weight:normal;font-size:10pt;color:black;border:solid 1px #808080;padding:4px;"&gt;&lt;span style="font-size:14pt;color:maroon;"&gt;&lt;i&gt;Parser Error&lt;/i&gt;&lt;/span&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Description: &lt;/b&gt;Error parsing a resource required to service this request. Review your source file and modify it to fix this error.
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Parser Error Message: &lt;/b&gt;Unknown attribute: MasterPageFile
&lt;br/&gt;&lt;br/&gt;
&lt;b&gt;Source Error:&lt;/b&gt;
&lt;div style="font-family:Lucida Console;font-size:9pt;background:#ffffaa;"&gt; 
&lt;span style="color:red;"&gt;Line 1: &amp;lt;%@ Page Language="C#" MasterPageFile="~/MasterPage.master" %&amp;gt; &lt;/span&gt;&lt;/div&gt;
&lt;hr width="100%" size="1" color="silver"&gt;&lt;b&gt;Version Information:&lt;/b&gt;&amp;nbsp;Mono Version: 1.1.4322.2032; ASP.NET Version: 1.1.4322.2032&lt;/div&gt;
&lt;br /&gt;
That was when I noticed the version number at the bottom. It 1.1 instead of 2.0. Just to make sure that Mono wasn't using a versioning system that was different from Microsoft's I created a simple page that used a common .NET 2.0 feature.
&lt;br /&gt;
&lt;pre&gt;&amp;lt;%@ Page Language="C#" %&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div&amp;gt;System.Version = &amp;lt;%= Environment.Version.ToString() %&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;List&amp;lt;string&amp;gt;.Count = &amp;lt;%= (new System.Collections.Generic.List&amp;lt;string&amp;gt;()).Count.ToString() %&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;
It got a compilation error. 
&lt;pre&gt;error CS1644: Feature `generics' is not available in Mono mcs compiler. Consider using Mono gmcs compiler instead&lt;/pre&gt;
Once I knew what the problem was I had to figure out how to fix it. I found a description of what to do &lt;a href="http://mono-project.com/Mod_mono"&gt;here&lt;/a&gt;. You have to change the MonoServerPath setting from &amp;lt;path&amp;gt;/mod-mono-server to &amp;lt;path&amp;gt;/mod-mono-server2 in the config file that contains it. This may be mod_mono.conf or httpd.conf or some other file. I am not sure where it is commonly placed.
&lt;br /&gt;&lt;br /&gt;
I am using shared web hosting so I don't have access to change or even view those files, so I contacted my web hosting company (&lt;a href="http://www.ubiquityhosting.com/"&gt;Ubiquity&lt;/a&gt;) and asked them to change it. They had me try changing it in my .htaccess file first, which I tried and found that it didn't work. The commands to start up the mod mono server so that it can handle ASP.NET requests must happen in a certain sequence in the config files so tacking it on after startup in an .htaccess file didn't work. It caused HTTP 500 errors for any requests to my directory. It may be possible to set up a fully working sequence of mod mono startup commands in an .htaccess file in order to override the ASP.NET version for a particular directory. I tried a number of different combinations while I was waiting for my hosting company to change it at the server level but none of the things I tried worked.
&lt;br /&gt;&lt;br /&gt;
They wanted to change it at the server level anyway because they advertise supporting ASP.NET 2.0 and I think that is how they had intended to set it up. I suspect that the commonly downloaded version of Mono is still set up to use ASP.NET 1.1 by default instead of 2.0. If that is the case they should change that so people who install it don't have to go find the setting and change it themselves.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-714974649883467583?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/714974649883467583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=714974649883467583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/714974649883467583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/714974649883467583'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/aspnet-11-and-20-on-mono.html' title='ASP.NET 1.1 and 2.0 on Mono'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-1246101241954366497</id><published>2008-11-19T10:08:00.000-08:00</published><updated>2008-12-12T21:23:19.058-08:00</updated><title type='text'>Setting up my site on Mono</title><content type='html'>I got my MySQL database set up through cPanel and PhpMyAdmin without any difficulty. I maintain re-runnable setup scripts for all my tables and stored procedures which makes life easier.
&lt;br/&gt;&lt;br/&gt;
I got FTP set up by downloading &lt;a href="http://winscp.net/"&gt;WinSCP&lt;/a&gt; for my FTP client and setting up a key in cpanel so I could use SFTP. Then I started uploading some files.
&lt;br/&gt;&lt;br/&gt;
I set up a file deployment batch file which would create a releasable package for easy copying. &lt;!-- todo: create post about deployment script and link to it from here --&gt;Then I uploaded all my web site files and tried it out.
&lt;br/&gt;&lt;br/&gt;
It didn&amp;#39;t work. The problem was that the server was configured to use ASP.NET 1.1 instead of 2.0. (See more about what I did to identify and fix this problem &lt;a href="/2008/11/aspnet-11-and-20-on-mono.html"&gt;here&lt;/a&gt;.)
&lt;br/&gt;&lt;br/&gt;
I opened a support ticket with my web hosting company (&lt;a href="http://www.ubiquityhosting.com/"&gt;Ubiquity&lt;/a&gt;) and exlained the situation to them and asked them to change it at the server level to use ASP.NET 2.0 by default. They did, but it took them a couple days to do it and get it working. I am guessing that most of the people that use their shared web hosting are not using Mono because it seemed like the support guys hadn't run into this issue before. If anyone else runs into it now they will probably be faster at fixing it, or better yet, they may change their default setup for new servers so that it works the right way automatically.
&lt;br /&gt;&lt;br /&gt;
Once the ASP.NET 1.1 / 2.0 issue was resolved I uploaded my main project to my directory again and gave it another try. That was when I encountered the next problem: my stored procedures I created in MySQL were not working. I'll put up new blog entry at some point explaining what the issue was there and how I resolved it. Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-1246101241954366497?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/1246101241954366497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=1246101241954366497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1246101241954366497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/1246101241954366497'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/setting-up-my-site-on-mono.html' title='Setting up my site on Mono'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-8207559070225660115</id><published>2008-11-19T09:44:00.000-08:00</published><updated>2008-11-24T00:33:25.163-08:00</updated><title type='text'>Choosing a web hosting provider</title><content type='html'>There are not too many companies that are offer shared web hosting with Mono. One of the ones that does is &lt;a href="http://www.ubiquityhosting.com/"&gt;Ubiquity&lt;/a&gt; and that is who I decided to go with. Their price was pretty good at $5.95 a month for a one year commitment with a 45 day money back guarantee plus free domain registration. That is $71.40 up front.
&lt;br/&gt;&lt;br/&gt;
I liked their feature set and the comments on the web about them seemed mostly favorable.
&lt;br/&gt;&lt;br/&gt;
After I went through their sign-up process I was excited to try it out and start playing around with it but when I tried the IP address they gave me it was not set up yet. I kept trying over the next couple days. I looked through their website to find out how long it would take and I saw that it could take up to 72 hours for some set ups and if it still wasn't set up after that to enter a support ticket.
&lt;br/&gt;&lt;br/&gt;
So I entered a support ticket and a few hours later someone got back to me and said it was working fine for them. I emailed them back that I was still unable to access it and that ping and tracert were not working. A few hours later they emailed me back and said my IP address had been blocked on their firewall and that it should be working now.
&lt;br/&gt;&lt;br/&gt;
Doh. I should have opened up a ticket sooner.
&lt;br/&gt;&lt;br/&gt;
It was still a ways away from getting things working at that point though. Read &lt;a href="/2008/11/setting-up-my-site-on-mono.html"&gt;more&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-8207559070225660115?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/8207559070225660115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=8207559070225660115' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/8207559070225660115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/8207559070225660115'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/choosing-web-hosting-provider.html' title='Choosing a web hosting provider'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7352323701912967109.post-7769396286852303841</id><published>2008-11-12T22:40:00.000-08:00</published><updated>2008-11-24T00:31:22.123-08:00</updated><title type='text'>Adventures in Mono</title><content type='html'>Last week I got my &lt;a href="http://twixtlive.com"&gt;TwixT&lt;/a&gt; website working well enough that I decided it was time to share with the world. I developed it in C# and ASP.Net, with a MySQL database and a Silverlight 1.0 app to for interactive game play.
&lt;br/&gt;&lt;br/&gt;
The idea behind the website was to make a place where people can play TwixT each other interactively, or play against the computer.
&lt;br/&gt;&lt;br/&gt;
I had it working pretty well on my local computer but to go live with it I needed a web hosting provider. The first thing I needed to decide was whether to choose a Linux or Windows based hosting. I decided to go with Linux because it was a little cheaper and from what I had read about Mono, it had almost all of the features in .Net 2.0, plus I thought it would be interesting to try.
&lt;br/&gt;&lt;br/&gt;
Read more about how I &lt;a href="/2008/11/choosing-web-hosting-provider.html"&gt;chose a web hosting provider&lt;/a&gt; and &lt;a href="/2008/11/setting-up-my-site-on-mono.html"&gt;tried to get my site working on Mono&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7352323701912967109-7769396286852303841?l=markcordell.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://markcordell.blogspot.com/feeds/7769396286852303841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7352323701912967109&amp;postID=7769396286852303841' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/7769396286852303841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7352323701912967109/posts/default/7769396286852303841'/><link rel='alternate' type='text/html' href='http://markcordell.blogspot.com/2008/11/adventures-in-mono-part-1.html' title='Adventures in Mono'/><author><name>Mark Cordell</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
