Nebulae Spiral

Spiralling Golden… and maybe blogging about code.

I like linux /ducks

with one comment

I’ll be honest about it, probably to my detriment, but I really do like linux.  I like the control I have over my system, the hardware, the kernel.  I like the ease of finding software and serving out content, I like apache, I like MySQL, I heart PHP but…  I especially like the eye candy.  Most of the time I’m running windows, which I like too, don’t get me wrong, more than linux for sheer ease of getting stuff done, be that familiarity or whatever.  They are different animals, maybe from different planets, both have their strengths and both have their flaws. 

I have to admit though, you can no longer complain about usability in linux, not with the ubuntu flavor.  If you are a standard user who wants to surf around, watch flicks, download some music, pron, whatever, you can do it  all just as easily in ubuntu as windows.  If you are a more adventurous user like myself who **needs** to have the pimp eyecandy 6 desktop rotating cube with secondlife on one and tv on the other and tool on the rest… well, thats where I rebooted back to xp.  At least if you know it isnt an option you dont frustrate yourself for hours trying to make it work right :-)  I could never EVER get my video card to work in ubuntu, not without crashing or freezing up or some sort of complaint.  I gave up. 

Turns out 8.10 is out though, so I’m in the middle of upgrading to the latest version of ubuntu, which apparently has those issues fixed in the latest ATI driver.  If this release supports my dual monitor set up – one digital, one VGA – without crashing when I go fullscreen across both of them, and letting me change the order of them would be cool as well –  the screens have been reversed for a few months – If this is as good as windows, well… my dual boot lets me access my windows hard drive and I can watch netflix on tv with my xbox now… I may not have to reboot for quite some time.  Secondlife will be the test.  My avie cannot be jagged, that is so last year.   

19 minutes remaining until installation complete.

building in secondlife is cool.  what is even cooler though is watching what kids build in secondlife.  I was watching my daughter playing on the teen grid the other day and she built this can of rootbeer with lava flowing inside it, bubbling up and out the sides.  it was rad.  Then she built herself a robot avie, and flew around as a big white robotic alien with huge black eyes.  Tres rad.  I was a proud moma.

5 minutes remaining. 

I wonder if anyone has tried to measure remote viewing or other psi phenomenon with people using windows as opposed to people using linux.    If particles and atoms can be influenced by intention, even become entangled, could that not reflect in the software thats written and the intention of the people writing it?  Writing code is not enormously different than writing poetry or painting or sculpting.  You cant deny that the feeling you get from one artists flower compared to another, or a song by the doors or the beatles or tool as compared to britney spears or any of the generic mass produced money-in-mind noise you hear in the top 40 at any given time.  You can feel the ripples of the intention behind it, the energy of the artist can be felt through the art, no? 

Restart system to complete upgrade.

So Ive spent the last half an hour trying to figure out how to get past the “composite extension not available” when activating the advanced desktop effects.  If anyones curious, you need to reconfigure the xserver-xorg settings: sudo dpkg-reconfigure xserver-xorg – once you run through this and it overwrites the previous settings, you can go pack into the advanced appearance settings and activate the super awesome “Extra” effects.  it’ll scan for plug ins and maybe prompt you for something or other, but it’ll work.  apparently xgl is dead, which makes me smile just a smidge because that was a problem for me before. 

run aticonfig and set the –desktop-setup=horizontal,reverse and a couple other little things, and restarted xserver and uh… monitors are in the right order.  looks pretty sweet!  Compiz fusion is running, I have the funky rad animations… but wheres my cube dammit?  go into compizconfig and not one checkbox is selectable for any of the eyecandy. 

ok, so 2 hours later after all sorts of silly googling and forum reading and reinstalls and updates, i found this post: http://ubuntuforums.org/showthread.php?t=590438 - and now i feel stupid.  CompizConfig -> preferences ->Plugin List -> check off automatic plugin sorting.  heheh.  and we have a cube… so hot. 

So, secondlife looks better than I remember… but still not awesome or as good asa in windows.  its acceptable though. 

Netflix: no.

http://fox.com/fod: no

http://abc.go.com : no

cbs.com?  YES!!  thank you cbs.

http://nbc.com: apparently not.

So this is totally awsome… except for the spontanious xserver restart when I do certain graphics intensive things… *sigh

I can remote desktop  into my laptop to run vpc’s to dev with, thats available albiet a little less than immediately responsive.  All things considered, I can’t use ubuntu for more than a couple hours at a time without becoming frustrated.  Its soooo pretty… but no iTunes, mostly unsupported streaming video, random xserver restarts and the time spent tracing them down to find a real solution, even if its a simple one, and the less than awesome secondlife experience… I think I’ll be booting back over to windows fairly soon.

Written by Nebulae

December 14th, 2008 at 3:31 pm

Posted in personal

RIP Bettie, and thanks for the revolution!

with one comment

Written by Nebulae

December 12th, 2008 at 12:34 pm

Posted in personal

fun with fractals

with one comment

check it out, kinda interesting.  This is what happens when I go on tangents.

Written by Nebulae

December 7th, 2008 at 4:23 am

WCF and Ajax and LINQ to SQL, oh my!

with 3 comments

If you haven’t taken a dive into the Windows Communication Foundation until now, this is a good place to start. I just recently dove in myself, and I found that accessing a .svc service from Ajax isn’t as straightforward as the old .asmx services, but it’s not that complicated either.

While we’re at it, lets look at some linq too.

As a testament to my detest of the usual customer – order database example that has been strewn all over the internet for examples such as these, lets do something a little more interesting and fun, how about a content rating system, from the ground up?

For this example, we’re going to pretend that we’re working with a database that already exists. you could reverse this action and use the visual studio interface to create your tables for you, but that’s a whole blog unto itself and I’m anxious to get to the Ajax part.  more info and an oh-so stimulating read here: http://msdn.microsoft.com/en-us/library/bb425822.aspx

Define some tables – we’ll need users, content, comments on said content, and a rating. a comment is content in itself, isn’t it?

To start off, create a database called “ContentRatingExample” and run these scripts against your sql server:

USE [ContentRatingExample]
GO
/****** Object:  Table [dbo].[Users]    Script Date: 12/06/2008 18:45:35 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Users](
	[ID] [uniqueidentifier] NOT NULL,
	[UserName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[DateCreated] [datetime] NOT NULL,
 CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
USE [ContentRatingExample]
GO
/****** Object:  Table [dbo].[Content]    Script Date: 12/06/2008 18:45:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Content](
	[ID] [uniqueidentifier] NOT NULL,
	[CreatedBy] [uniqueidentifier] NOT NULL,
	[Content] [nvarchar](max) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[Title] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[DateCreated] [datetime] NOT NULL,
	[DateModified] [datetime] NOT NULL,
	[Parent] [uniqueidentifier] NULL,
 CONSTRAINT [PK_content] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
USE [ContentRatingExample]
GO
/****** Object:  Table [dbo].[Ratings]    Script Date: 12/06/2008 18:46:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Ratings](
	[ID] [uniqueidentifier] NOT NULL,
	[UserID] [uniqueidentifier] NOT NULL,
	[ContentID] [uniqueidentifier] NOT NULL,
	[Rating] [tinyint] NOT NULL,
	[DateCreated] [datetime] NOT NULL,
 CONSTRAINT [PK_Ratings] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Have a look, pretty simple little database. A table for users, a table for Content, a table for ratings. Note that the Content table has a parent column, this will be a reference to the id of the content so we can recurse and get all the comments for a content object, and all of their comments, allowing n-depth threading. The user db is really simplistic, we don’t care about user profiles or anything at this point other then a friendly name to display. The rest is self explanatory.

Lets jump over to visual studio now and create a new project. Select a Class Library project and call it Common, Create a new solution for it and Call that ContentRating, and make sure “Create Directory for solution” is checked.

Now, Add another project to that solution, call it “ContentRatingService” and select the type “WCF Service Application” from the Visual C# -> Web Templates.

Then add a website to the solution, call it UI.

To this UI project, add a couple new folders: Includes and UserControls. Inside the Includes folder, add 2 more folders: images and scripts.

your solution should now look like this:

Now we just need to do a little cleanup and the skeleton of our solution will be ready.

To the “Common” Project:

  1. Right click on the Common project and select properties.
  2. Under the Application tab, change the Assembly name and the Default Namespace to “ContentRating.Common” and save.
  3. Delete Class1.cs

To the “ContentRatingService” project:

  1. Right click on the Common project and select properties.
  2. Under the Application tab, change the Assembly name and the Default Namespace to to ContentRating.Service and save.
  3. Delete Service1.svc and IService1.cs
  4. Add a reference to the Common project

To the UI project:

  1. Right click on the Common project and select properties.
  2. Under the Application tab, change the Assembly name and the Default Namespace to ContentRating.UI and save – are you seeing a pattern here?
  3. Delete Default.aspx

Your solution should now look like:

Build the solution and make sure that the assembly names generated are ContentRating.UI.dll, ContentRating.Common.dll, and ContentRating.Service.dll

ok, now we can start :-)

The first project we’re going to modify is the “Common” project.  Right click on that bad boy and go “Add New Item”.  Select “Linq to SQL classes” and name it “ContentRatingClasses.dbml”.

open up the server explorer and right click “Data Connections” -> “Add Connection”.  Select “Microsoft SQL Sever” as the Data source and the .NET framework Data Provider for SQL as the Data Provider.   Hit Continue.

select your server name, the ContentRatingExample db, and test the connection to make sure its all good, click ok.

expand and drill down the dataconnections in the server explorer and you should see your tables:

if not, you’ve done something wrong and need to fix it before continuing.

drag your tables onto the Object Relational Designer, and you’ll see them drawn out all pretty with the relationships defined.

** note: because I have named some of the columns similar to the table name, visual studio took it upon itself to change the names for a couple of the properties: in the Ratings table, Ratings is now Rating1, and in the Content table, Content is now Content1. my bad. I should have conformed to a more robust naming convention.

Build and then have a look at the code behind by opening ContentRatingClasses.designer.cs. you’ll see all the tables here have been generated into classes, and you should notice a lot of opportunity in this file – a lot more than we’re going to get into today but have a look at the partial classes and the partial methods for OnCreated, OnChanging and OnChanged, etc. obviously, if you were to create another partial class of this type on the same namespace and implement the partial method, you’d be able to execute some code on each of these events. Keep that in mind.

Here is where we stop and contemplate a few things.  In our service we are yet to create, we will need to return deserialized entities to be consumed by the client, in this case an Ajax application.  There is the option of creating a custom object to populate with the result of the linq query to these objects that the dbml generated, and apply the [DataContract] and [DataMember] attributes to those objects and properties respectively, so they can be deserialized and used at the other end.  This seems a little silly though, as it would be redundant, we already have the objects and types defined, why rewrite them?  Instead, lets take the easy way out:

double click the ContentRatingClasses.dbml file and right click anywhere inside the designer pane that has empty space and click properties, or otherwise fine the DataContext properties for the dbml.  Set the serializationMode to Unidirectional, save, and build.

you’ll see that all of the objects in the ContentRatingClasses.designer.cs file now have the DataContract() attribute, and the properties have the DataMember() attribute, and we’re golden :-)

        [Table(Name="dbo.Content")]
	[DataContract()]
	public partial class Content : INotifyPropertyChanging, INotifyPropertyChanged
	{
                .
                .
                .
                [Column(Storage="_ID", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
		[DataMember(Order=1)]
		public System.Guid ID
		{
                    .
                    .
                    .

Righto – onto the service.

in the ContentRatingService project, add a new item – select WCF Service, name it Service.svc. You’ll notice that IService.cs and Service.svc were created and added to the project. IService.cs behaves as any other interface, add your rules here for the services you will expose. a default DoWork service was created for you, with the attribute OperationContract.   First off, add a reference to System.ServiceModel.Web.  This will provide us with the Attribute we need to define our response format: WebInvoke(). Add some more contracts:

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using ContentRating.Common;
 
namespace ContentRating.Service
{
    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        List GetUsers();
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        User GetUser(Guid id);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddUser(string username);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        List GetMainContent();
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        List GetChildContent(Guid ParentID);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddContent(Guid userID, string title, string text);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddComment(Guid userID, string title, string text, Guid contentID);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        List GetRatingsForContent(Guid ContentID);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        List GetRatingsFromUser(Guid UserID);
 
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddRating(Guid contentID, Guid userID, byte rated);
    }
}

The next step is to implement these members in the object:

using System;
using System.Collections.Generic;
using System.Linq;
using ContentRating.Common;
 
namespace ContentRating.Service
{
    public class Service : IService
    {
        public List GetUsers()
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var users = (from u in ctx.Users
                             select u).ToList();
                return users;
            }
        }
 
        public User GetUser(Guid id)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var user = (from u in ctx.Users
                            where u.ID == id
                             select u).Single();
                return user;
            }
        }
 
        public void AddUser(String username)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                User user =new User {DateCreated = DateTime.Now, ID = Guid.NewGuid(), UserName = username};
 
                ctx.Users.InsertOnSubmit(user);
                ctx.SubmitChanges();
            }
        }
 
        public List GetMainContent()
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var mainContent = (from content in ctx.Contents
                                   where content.Parent == null
                                   select content).ToList();
                return mainContent;
            }
        }
 
        public List GetChildContent(Guid ParentID)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var childContent = (from content in ctx.Contents
                                   where content.Parent == ParentID
                                   select content).ToList();
                return childContent;
            }
        }
 
        public void AddContent(Guid userID, string title, string text)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                Content content = new Content
                                      {
                                          ID = Guid.NewGuid(),
                                          DateModified = DateTime.Now,
                                          DateCreated = DateTime.Now,
                                          CreatedBy = userID,
                                          Title = title,
                                          Content1 = text
                                      };
                ctx.Contents.InsertOnSubmit(content);
                ctx.SubmitChanges();
            }
        }
 
        public void AddComment(Guid userID, string title, string text, Guid contentID)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                Content content = new Content
                                      {
                                          ID = Guid.NewGuid(),
                                          DateModified = DateTime.Now,
                                          DateCreated = DateTime.Now,
                                          CreatedBy = userID,
                                          Title = title,
                                          Parent = contentID,
                                          Content1 = text
                                      };
                ctx.Contents.InsertOnSubmit(content);
                ctx.SubmitChanges();
            }
        }
 
        public List GetRatingsForContent(Guid ContentID)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var ratings = (from rating in ctx.Ratings
                                    where rating.ContentID == ContentID
                                    select rating).ToList();
                return ratings;
            }
        }
 
        public List GetRatingsFromUser(Guid UserID)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                var ratings = (from rating in ctx.Ratings
                               where rating.UserID == UserID
                               select rating).ToList();
                return ratings;
            }
        }
 
        public void AddRating(Guid contentID, Guid userID, byte rated)
        {
            using (ContentRatingClassesDataContext ctx
                = new ContentRatingClassesDataContext())
            {
                Rating rating = new Rating
                                    {
                                        ID = Guid.NewGuid(),
                                        DateCreated = DateTime.Now,
                                        ContentID = contentID,
                                        UserID = userID,
                                        Rating1 = rated
                                    };
                ctx.Ratings.InsertOnSubmit(rating);
                ctx.SubmitChanges();
            }
        }
    }
}

now open up the web.config in the ContentRatingService project. near the bottom of the file, you’ll see a service and a behavior. You’ll need to modify it to look like this:

<system.serviceModel>
<services>
<service behaviorConfiguration=”ContentRating.Service.ServiceBehavior” name=”ContentRating.Service.Service”>
<endpoint address=”" binding=”wsHttpBinding” contract=”ContentRating.Service.IService”>
<identity>
<dns value=”localhost”/>
</identity>
</endpoint>
<endpoint address=”mex” binding=”mexHttpBinding” contract=”IMetadataExchange”/>

<!– the ajax endpoint –>
<endpoint address=”ajaxEndpoint” behaviorConfiguration=”ajaxBehavior” binding=”webHttpBinding” contract=”ContentRating.Service.IService” />

</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name=”ContentRating.Service.ServiceBehavior”>
<serviceMetadata httpGetEnabled=”true”/>
<!– changed to true so you can see anything that goes wrong –>
<serviceDebug includeExceptionDetailInFaults=”true”/>
</behavior>

</serviceBehaviors>

<!– the ajax endpoint behavior –>
<endpointBehaviors>
<behavior name=”ajaxBehavior”>
<enableWebScript />
</behavior>
</endpointBehaviors>

</behaviors>
</system.serviceModel>

and that’s all for the service.

I should have mentioned before,  or maybe i wanted you to get this far before breaking the news,  the Ajax implementation of this application will NOT be using the microsoft ajax dll or anything of that nature. Ajax is easy and simple and you don’t need someone to handle it for you, and you should understand how it works from the inside anyway.

Go here and get the best cross browser ajax library you could need:

http://code.google.com/p/xmlhttprequest/

and extract it to your Includes/scripts folder in the UI project.  This is only javascript, nothing to be scared of.  I will never understand the irrational fear of javascript some people have… anyway, add a file to your UI project, just a normal html file, call it default.html, or index.html, or whatever you want, it doesnt matter to me.  in the head section of that file, add a script reference the the XMLHttpRequest.js file you just saved.

Ajax is sandboxed, meaning that a request can only be made to the current server, you can’t cross domains.  to avoid any pain and suffering through running the site in debig mode by pressing F5 or play in visual studio, the next step is to create an site on localhost of the machine youre running this on so we don’t have any comminuication issues.  Go into IISManager, right click your server instance and select create new website.  go with the defaults for port 80 and set the path to be the root path of your project.  Allow anonymous access, and select Read, Run, and Browse.  Click finish to create it.  Make sure that the site is set to use asp.net 2 and not 1 in the asp.net settings tab under properties.  You should have a site that resembles this (without the resharper files unless you use resharper.. and if you dont, uhm… wtf?):

now right click on the UI folder, click properties, click create to create it as an application.  do the same for the ContentRatingService folder.  you should have this:

surf over to http://localhost/ContentRatingService/Service.svc and you should see a “Service Service” definition – ahha! those pesky bad naming conventions habits of mine… my bad… then go here http://localhost/ContentRatingService/Service.svc?wsdl or just click the link on the svc page. There’s a lot of crazy info in there, the blatantly useful stuff is at the bottom.

go back to the default.html page4 in the UI project, and add some javascript in the head (please remove the code tags, they are there so the xml doesnt get parsed out by the browser, the javascript will break if you leave them in. ) :

function AddUser(username) {
<code>
        var client = new XMLHttpRequest;
 
        var data = '' + username + '';
        client.open('POST', 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/AddUser', true);
        client.setRequestHeader("Content-length", data.length);
 
        client.setRequestHeader("Content-type", "text/xml");
        client.onreadystatechange = function() {
            if (client.readyState == 4 || client.readyState == XMLHttpRequest.DONE) {
                alert(client.responseText);
            }
        }
 
        client.send(data);
 
    }
 
    function GetUsers() {
        var client = new XMLHttpRequest;
 
        var data = '';
        client.open('POST', 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/GetUsers', true);
        client.setRequestHeader("Content-length", data.length);
 
        client.setRequestHeader("Content-type", "text/xml");
        client.onreadystatechange = function() {
            if (client.readyState == 4 || client.readyState == XMLHttpRequest.DONE) {
                alert(client.responseText);
            }
        }
 
        client.send(data);
 
    }
 
</code>

and then in the body tags, create a couple controls to interact with this javascript:

<input type=button onclick="GetUsers()" value="Get Users" />
<br /><br /><input type=text id='username' />
<input type=button onclick="AddUser(document.getElementById('username').value)" value="Add User" />

When you surf over to http://localhost/UI/default.html you should see:

when you click the Get Users Button, you will be alerted:

since we have no users, nothing was returned.

add a name to the text box and then click Add User.  you will see a blank alert, or an error.  a blank alert is good.  click Get Users again, and you will see:

with the name of the user you just added.  pretty cool huh?

you can compare the data string sent into the ajax call to the properties that the service is expecting, and note that they are exactly the same, these are case sensitive.

Head back over to the ContentRatingClasses.dbml and right click on one of the tables to view code. the file ContentRatingClasses.cs should come up, and into that file we want to implement an average rating property on the Content class.

using System;
using System.Linq;
using System.Runtime.Serialization;
 
namespace ContentRating.Common
{
    public partial class Content
    {
        [DataMember]
        public decimal AverageRating
        {
            get; set;
        }
 
        partial void OnLoaded()
        {
            using( ContentRatingClassesDataContext ctx = new ContentRatingClassesDataContext() )
            {
                var ratings = from r in ctx.Ratings
                              where r.ContentID == this.ID
                              select r;
 
                var number = ratings.Count();
                int total = 0;
                foreach(var rate in ratings)
                {
                    total += rate.Rating1;
                }
                decimal average = 0;
                if(decimal.TryParse((total/number).ToString(), out average))
                {
                    AverageRating = average;
                }
                AverageRating = 0;
 
            }
        }
    }
}

At this point, all the functionality you will need on the server is complete. the rest is javascript – some ajax calls and a little dhtml.

go back to your default.html page, and replace the script between the head tags with this:

        function SendRequest(data, postUrl, callBack, args) {
            var client = new XMLHttpRequest;
            client.open('POST', postUrl, true);
            client.setRequestHeader("Content-length", data.length);
 
            client.setRequestHeader("Content-type", "text/xml");
            client.onreadystatechange = function() {
                if (client.readyState == 4 || client.readyState == XMLHttpRequest.DONE) {
                    callBack(client.responseXML, args);
                }
            }
 
            client.send(data);
        }
 
        function AddComment(title, comment, userID, contentID) {
 
            var data = '<AddComment xmlns="http://tempuri.org/"><userID>' + userID + '</userID><title>' + title + '</title><text>' + comment + '</text><contentID>' + contentID + '</contentID></AddComment>';
            var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/AddComment';
            var args = new Array();
            args[0] = contentID;
            SendRequest(data, postUrl, GetAllMainContent, args);
        }
 
        function GetAllMainContent() {
 
            document.getElementById('content').innerHTML = '';
            var data = '<GetMainContent xmlns="http://tempuri.org/"></GetMainContent>';
            var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/GetMainContent';
            SendRequest(data, postUrl, DrawContent, null);
        }
 
        function GetChildContent(parentID) {
 
            var data = '<GetChildContent xmlns="http://tempuri.org/"><ParentID>' + parentID + '</ParentID></GetChildContent>';
            var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/GetChildContent';
            var args = new Array();
            args[0] = parentID;
            SendRequest(data, postUrl, DrawContent, args);
 
        }
 
        function SubmitRating(contentID) {
            RateContent(contentID,
                document.getElementById('users').options[document.getElementById('users').selectedIndex].value,
                document.getElementById('rating_' + contentID).options[document.getElementById('rating_' + contentID).selectedIndex].text);
        }
 
 
        function RateContent( contentID, userID, rated ) {
 
            var data = '<AddRating xmlns="http://tempuri.org/"><contentID>' + contentID + '</contentID><userID>' + userID + '</userID><rated>' + rated + '</rated></AddRating>';
            var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/AddRating';
            SendRequest(data, postUrl, DrawContent, null);
 
        }
 
        function AddContent(title, content, userID) {
 
            var data = '<AddContent xmlns="http://tempuri.org/"><userID>' + userID.toUpperCase() + '</userID><title>' + title + '</title><text>' + content + '</text></AddContent>';
            var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/AddContent';
 
            SendRequest(data, postUrl, GetAllMainContent, null);
        }
 
        function SubmitComment(contentID) {
            AddComment(document.getElementById('title_' + contentID).value,
                document.getElementById('reply_' + contentID).value,
                document.getElementById('users').options[document.getElementById('users').selectedIndex].value,
                contentID);
        }
 
 
    function AddUser(username) {
 
        var data = '<AddUser xmlns="http://tempuri.org/"><username>' + username + '</username></AddUser>';
        var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/AddUser';
        SendRequest(data, postUrl, GetUsers, null);
    }
 
    function GetUsers() {
 
        var data = '<GetUsers xmlns="http://tempuri.org/"></GetUsers>';
        var postUrl = 'http://localhost/ContentRatingService/Service.svc/ajaxEndpoint/GetUsers';
        SendRequest(data, postUrl, PopulateUsers, null);
    }
 
 
 
    function DrawContent(responseXML, args) {
 
        var contentNodes = responseXML.getElementsByTagName('Content');
 
        if (!!contentNodes && contentNodes.length > 0) {
            for (var i = 0; i < contentNodes.length; i++) {
 
                var id = contentNodes[i].getElementsByTagName('ID')[0].firstChild.nodeValue;
 
                var div = document.createElement('div');
                div.style.border = '1px solid #CCCCCC';
                div.setAttribute('id', id);
                div.style.margin = '25px';
                div.style.padding = '5px';
 
                var b = document.createElement('b');
                b.innerHTML = contentNodes[i].getElementsByTagName('Title')[0].firstChild.nodeValue;
                div.appendChild(b);
 
                div.innerHTML += " - rating: " + contentNodes[i].getElementsByTagName('AverageRating')[0].firstChild.nodeValue;
 
                div.innerHTML += " - rate: ";
 
                var select = document.createElement('select');
                select.setAttribute('id', 'rating_' + id);
                for (var x = 1; x < 11; x++) {
                    var option = new Option(x, x);
                    select.options[select.options.length]= option;
                }
 
                div.appendChild(select);
 
 
 
                div.innerHTML += '<a href="javascript:SubmitRating(\'' + id + '\')">submit</a><br />';
                div.innerHTML += "<p>" + contentNodes[i].getElementsByTagName('Content1')[0].firstChild.nodeValue + "</p>";
 
                div.innerHTML += '<br /><br />reply to this:<br /><input type="text" id="title_' + id + '" /><br /><textarea id="reply_' + id + '"></textarea><br />';
                div.innerHTML += '<a href="javascript:SubmitComment(\'' + id + '\')">submit</a>';
 
                if (args == null) {
                    document.getElementById('content').appendChild(div);
                    GetChildContent(id);
                } else {
                    var parentID = args[0];
                    document.getElementById(parentID).appendChild(div);
                    GetChildContent(id);
                }
 
 
            }
        } 
    }
 
    function PopulateUsers(responseXML, args) {
 
        var userNodes = responseXML.getElementsByTagName('User');
        if (!!userNodes && userNodes.length > 0) {
            document.getElementById('users').innerHTML = '';
 
            for (var i = 0; i < userNodes.length; i++) {
 
                var option = new Option(userNodes[i].getElementsByTagName('UserName')[0].firstChild.nodeValue, userNodes[i].getElementsByTagName('ID')[0].firstChild.nodeValue);
                var count = document.getElementById('users').options.length;
                document.getElementById('users').options[count] = option;
            }
        } 
    }
 
    function PrepPage() {
        GetUsers();
        GetAllMainContent();
    }

and then replace the body:

<body onload="PrepPage()">
    be user: <select id='users'></select>
    <input type="text" id='username' />
    <input type="button" onclick="AddUser(document.getElementById('username').value)" value="Add User" />
 
    <div id='content'>
 
    </div>
    <br />
    <br /><br />new content:<br />
    <input type="text" id="title" />
    <br /><textarea cols='30' rows='3' id="reply"></textarea>
    <br /><input type="button" onclick="AddContent(document.getElementById('title').value, document.getElementById('reply').value,document.getElementById('users').options[document.getElementById('users').selectedIndex].value)" value="Add" />
 
 
</body>

what you will see will be ugly but functional, and pretty much self explanitory.

complete solution here, all you’ll have to do is create the db, run the scripts above, and create the sites and apps in iis: contentratingservice

Enjoy! any questions, requests, please comment.

peace,
neb

Written by Nebulae

December 6th, 2008 at 10:14 pm

Posted in code

Tagged with , , , ,

The Rhetoric and the Reality – Interactive Graphic – NYTimes.com

without comments

Reality Check

John McCain

Republican convention speech, Sep. 4

I will keep taxes low and cut them where I can. My opponent will raise them.”

Reality Check

This drastically simplifies what the candidates’ tax plans would do. Mr. McCain would preserve all of the Bush tax cuts, while Mr. Obama would let them expire for those making more than $250,000 a year. Mr. McCain would also double the child tax exemption to $7,000 and reduce business taxes. Mr. Obama would reduce income taxes and provide credits for people earning less than $250,000 a year. The nonpartisan Tax Policy Center found that Mr. Obama’s plan would amount to a tax cut for 81 percent of all households, or 95.5 percent of those with children. The center calculated that by 2012 the Obama plan would let middle-income taxpayers keep about 5 percent more income on average, or nearly $2,200 a year, while Mr. McCain would give them an average 3 percent break, or about $1,400. The richest 1 percent would pay an average $19,000 more in taxes each year under Mr. Obama’s plan but see a tax cut of more than $125,000 under Mr. McCain.

please do read more:

The Rhetoric and the Reality – Interactive Graphic – NYTimes.com.

Written by Nebulae

September 12th, 2008 at 9:37 am

Posted in personal

Tagged with

People in the Sun: On the 2008 RNC Convention

with one comment

Saying Palin’s nomination is historic because she’s a woman is like saying the last eight years have been historic because it was the first time an idiot man-child has been allowed to rule a nation.

People in the Sun: On the 2008 RNC Convention.

Written by Nebulae

September 12th, 2008 at 9:33 am

Posted in personal

Tagged with

without comments

Ive notice a trend in the people hitting this site… they are looking for sharepoint info, not updates on my personal life.  Ive made private posts hidden to anonymous users, but if you do care for updates on the life of trinity, please register to see them.  

Apparently the Save Conflict is a pretty common search hit, I’m sorry i dont have a solution or cause to this but i can do some more reserch and try to narrow down a good solution.

cheers

trinity

Written by Nebulae

September 11th, 2008 at 12:02 pm

Posted in random

RPC_E_ATTEMPTED_MULTITHREAD

with 3 comments

Exception information:
Exception type: COMException
Exception message: Attempted to make calls on more than one thread in single threaded mode. (Exception from HRESULT: 0×80010102 (RPC_E_ATTEMPTED_MULTITHREAD))

ooh yah baby. this ones a doozy.

If you’re running into this in your sharepoint development journey, you have well bypassed the open site / open list / get list item / do stuff with it stage and have moved into the world of creating object representations of your list items, probably databinding that object off the list item to work with it, then populating the listitem columns from the object before saving it. IMHO, this is a nice, clean and managable way of development, BUT you could run into a couple roadblocks. this one looks a tad daunting but its an easy one to get around.

My scenario uses 3 lists. Registrations, Organizations, and Solutions. An Organization has an owner (registration) and 0-many solutions. I know, I know, the organization should be a property of the registration, but i digress…

so i have an organization object. this object has a bunch of fancy properties – address, phone number, etc, etc, and also an Owner and a List<Solution> . During the databind of this object from the listitem, I attempted to set these objects and received the RPC_E_ATTEMPTED_MULTITHREAD error – if you google this error, which you probably have since you landed here, you’ll see some answers that will have you believe that you need to implicitly dispose of the SPSite, SPWeb – you may even be tempted to add a GC.Collect in there.

This didn’t – and still doesn’t – make a whole lot of sense to me because the access to the list item was wrapped in a using statement which of course only works with types that inherit from IDisposable, and will be disposed of when you exit the using statement, right? Right? so you would think that after you access that list item and leave the using statement, the SPSite is disposed of and youre good to go… which is not really the case and I don’t know the intricacies of it all but I do know how to bypass this problem and the answer is – Lazy Loading. yes, serious.

back to my databind…
as I am binding all the properties from the listitem i just retrieved and I try to set the owner which would have to go access that site and get that list item from the registrations list, create a new owner object from that item, the RPC_E_ATTEMPTED_MULTITHREAD error gets thrown. the same when I’m trying to populate the Associated Solutions. If you move the code to populate these properties into the public accessor ( the getter ) so that the property is loaded when it is accessed instead of when the object is databound – poof! problem solved.

If this doesn’t solve your problem, or you’re seeing this error in Sharepoint without having the structure i described above or some format of it, drop me a line and we can figure it out.

cheers,
trinity
err, neb

Written by Nebulae

September 4th, 2008 at 10:02 am

Posted in code

Tagged with , , ,

Infopath forms hang when submitting to server.

with one comment

Ever run into the problem where you fill out a web based infopath form and it just… hangs… sending data to server? probably an issue with SP1. if you check the SPLogs, you’ll probably see errors about the canary being modified or something otherwise cryptic and useless: The format of the Canary has been tampered with for form {some guid here}

I dealt with this last week, did some research, and was advised to replace the SP1 Microsoft.Office.InfoPath.Server.dll with the pre SP1 infopath dll on the server. this seemed to fix the problem, but who knows what other issues it introduced. I would love to hear if anyone else has run into this, and if they managed to solve it in another, less hackey way.

Written by Nebulae

September 2nd, 2008 at 12:35 pm

Posted in personal

Handy SPFolder / SPList / SPFile / SPWhatever interface, using Generics and Elevated Privs.

with one comment

This is a handly little lib I wrote for accessing various types of objects using generics. enjoy :-)

class SPDataAdapter
{
    public static T GetObject(string location)
    {
        object obj = null;
        SPSecurity.RunWithElevatedPrivileges(delegate
        {
            using (var site = new SPSite(location))
            {
                obj = site.OpenWeb().GetObject(location);
            }
        });
        return (T)obj;
    }
 
    public static T GetObject(string location, bool useElevatedPrivs)
    {
        if (useElevatedPrivs) return GetObject(location);
        using (var site = new SPSite(location))
        {
            return (T)site.OpenWeb().GetObject(location);
        }
    }
 
    public static SPList GetList(string location, bool useElevatedPrivs)
    {
        if (useElevatedPrivs) return GetList(location);
        using (var site = new SPSite(location))
        {
            return site.OpenWeb().GetList(location);
        }
    }
 
    public static SPList GetList(string location)
    {
        SPList list = null;
        SPSecurity.RunWithElevatedPrivileges(delegate
       {
            using (var site = new SPSite(location))
            {
                list = site.OpenWeb().GetList(location);
            }
        });
 
        return list;
 
   }
 
}

and to test:

 
[TestMethod()]
public void GetObjectTest1()
{
    GetObjectTest1Helper<SPFolder>();
}
 
public void GetObjectTest1Helper<T>()
{
    const string location = "http://spiral/Lists/Tasks";
    const bool useElevatedPrivs = true;
    var actual = SPDataAdapter.GetObject<T>(location, useElevatedPrivs);
    Assert.IsNotNull(actual);
}
 
[TestMethod()]
public void GetObjectTest()
{
    GetObjectTestHelper<SPFile>();
}
 
public void GetObjectTestHelper<T>()
{
    const string location = "http://spiral/Lists/Tasks/Attachments/1/nebulae.jpg";
    T actual = SPDataAdapter.GetObject<T>(location);
    Assert.IsNotNull(actual);
}

Written by Nebulae

August 28th, 2008 at 11:01 am

Posted in code

Tagged with , ,