Justin Toth's Blog

Justin is a web developer living in Maryland

Silverlight 3 Released!

clock July 10, 2009 14:18 by author Justin Toth

Just as promised, Silverlight 3 has been released, actually 1 day earlier than the expected July 10th date. You can get the latest tools here.

For those of us who have been already playing with Silverlight 3 beta, there are some breaking changes. The most obvious one is that the System.Web.Silverlight assembly has been removed, meaning that the ASP.NET Silverlight control that we were using won't work anymore. Alternately, you need to set up your Silverlight app using an object and an iframe tag. The object tag shouldn't be new to anyone and was always an alternative to using the Silverlight control. The iframe will be used to handle browser history, which comes along with the new SL3 Navigation features.

To view a full list of the changes, go here:

http://docs.google.com/View?id=dnkk749_0czvc86gx



To have one's cake and eat it too...

clock June 11, 2009 22:42 by author Justin Toth

The past month or two I've been struggling with an architectural issue and haven't been able to find a good answer. The fact of the matter is, Silverlight, WCF, and the Entity Framework don't play nice together. Here's why...

You start out by adding your Entity Model to your WCF app or an assembly that WCF references. You expose your EF business objects from your WCF methods and consume them from your Silverlight app. The problem is that this will generate the EF business objects under each service proxy namespace rather than in one central namespace. Here's an example of this case:

Service Proxy Namespace Hell:

Let's say you have 2 ADO.NET Data Services - UserService and GeographicService.

There's an Add User form that has a combobox with all of the states. You call GeographicServiceProxy.GetStates(), which returns List<GeographicServiceProxy.State>. You bind the combobox to this list.

When Save is pressed, you construct a UserServiceProxy.User object which you'll then send to UserServiceProxy.Add(user). The problem is that you now can't set the UserServiceProxy.User.State property, as your dropdown contains GeographicServiceProxy.State objects. You'll get a build error if you try to assign directly between them and you end up having to manually set each property from one to the other, which doesn't work when we're talking about an entire application. Basically, all of your business objects are duplicated in the namespace of each service proxy and you can't directly assign between them due to the different namespaces, aka Service Proxy Namespace Hell.

The solution in a normal client environment (not Silverlight) would be to reference the project containing the business objects, then when adding the Service References, so long as you have "Reuse types in all referenced assemblies" selected, it won't generate the methods and will correctly have the business object types be of your referenced assembly types, such as MyNamespace.BusinessObjects.MyBusinessObject, rather than within the individual service proxy namespaces, such as MyNameSpace.Silverlight.Service1Proxy.MyBusinessObject.

Silverlight half-baked referencing:

However since Silverlight doesn't let you reference a non-Silverlight assembly, this doesn't fly. Let's say you try to get around this by creating a Silverlight class library and adding a link to the Entity Model (edmx) file. The Silverlight class library won't build unless you manually edit the project file and add references to .NET assemblies such as System and System.Data. Now you reference this Silverlight class library within your Silverlight app and you end up with build errors because you now have clashing Silverlight and non-Silverlight assemblies, such as .NET's "System" vs. Silverlight's "system". Dead end...

The alternative, and the method I'm currently using, is to create custom business objects that match the EF business objects. You can then expose these from your WCF service methods. Let's say you wanted to call a WCF Add(entity) method. You would pass in a custom business object, convert it property by property to an EF business object, then save it. If you wanted to call a WCF Get() method, you would use linq to get the EF business object, then property by property you'd convert it to a custom business object to return from the method. As you can see, this results in a lot of extra code being written and you now have opened up your WCF/EF code to having to deal with objects property by property, losing some of the beauty. Now that your WCF service methods are exposing custom business objects, you need your Silverlight application to reference the custom business objects. What you have to do is create a new Silverlight class library project, doing add existing items, selecting all of your business business object classes, and doing add as link (notice the down arrow next to the Add button.) Now you can reference this new Silverlight class library within your Silverlight application and add your service references, which will generate the proxy business objects within their correct namespace so long as you have set "Reuse types in all referenced assemblies".

Alternatives to WCF:

 So you say, why not try something else besides WCF to get around this issue? Certainly, you could. Let's say you put ADO.NET Data services within your WCF project. You'll still run into the same service proxy namespace issue. You could use RIA services, but then you're demoting your services code to running on an ASP.NET web app that is hosting the Silverlight app. Say goodbye to all remnants of SOA and supporting multiple UIs...

You can't have your cake and eat it too:

 So here's what I want...

1. To use the Entity Framework for data access and ONLY use its generated business objects, not having to write my own duplicate custom business objects.

2. To use WCF or ADO.NET Data Services so that I can support multiple UIs in the future and to be able to expose the Entity Framework entities from the services rather than having to convert between the EF entities and my custom business objects.

3. To use Silverlight for the UI without having my entities split up and duplicated in each generated service proxy namespace.

The key to resolving all of these issues is #3. If MSFT added support for referencing the WCF services project within the Silverlight app, then you could add your service references and they would generate the EF entities in their appropriate namespace. However, sadly, this isn't the case, and we're left with 3 technologies that are great on their own but don't end up playing nice together. I wish the teams for these different products worked closer together, because I can see the beauty in each of these technologies, yet together you end up having to make compromises...



WCF/Silverlight Exception Handling

clock June 10, 2009 16:09 by author Justin Toth

The past couple of days I've been struggling setting up proper exception handling in WCF and Silverlight. In WCF I wanted to handle exceptions in 1 place rather than having to try/catch in every single service method. I was able to accomplish this using behaviors and the IErrorHandler:

Here's the WCF code:

Services/Models/Constants.cs:

public class Constants
    {
        public const string FaultAction = "//ErrorHandler/FaultAction">http://ErrorHandler/FaultAction";
    }

 

Services/Interfaces/ICategoryService.cs: (sample WCF service interface)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using CouponJoe.Schemas;
using CouponJoe.Schemas.Results;

namespace CouponJoe.Services.Interfaces
{
    [ServiceContract]
    public interface ICategoryService
    {
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        Category Add(Category category);
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        Category Update(Category category);
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        DeleteResult Delete(Category category);
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        Category Get(int categoryId);
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        Category GetByName(string categoryName);
        [OperationContract]
        [FaultContract(typeof(string), Action = Models.Constants.FaultAction)] 
        List<Category> Search();
    }
}

Services/Behaviors/ErrorHandler.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using CouponJoe.Services.Models;

namespace CouponJoe.Services.Behaviors
{
    public class ErrorHandler : ErrorHandlerBehavior, IErrorHandler, IServiceBehavior
    {

        public bool HandleError(Exception error)
        {
            //TODO: log exception.
            return true;
        }
        
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            string errorMessage = String.Empty;
            if (error.InnerException == null)
            {
                errorMessage = String.Format("{0}: {1}", error.GetType().FullName, error.Message);
               
            }
            else
            {
                errorMessage = String.Format("{0}: {1}", error.InnerException.GetType().FullName, error.InnerException.Message);
            }
            FaultException<string> faultException = new FaultException<string>(errorMessage, new FaultReason(errorMessage));
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, Models.Constants.FaultAction);
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach(ChannelDispatcher channDisp in serviceHostBase.ChannelDispatchers)
            {
                channDisp.ErrorHandlers.Add(this);
            }
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> serviceEndPoints, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            //do nothing.
        }

    }
}

Services/Behaviors/ErrorHandlerBehavior.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Configuration;

namespace CouponJoe.Services.Behaviors
{
    public class ErrorHandlerBehavior : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get
            {
                return typeof(ErrorHandler);
            }
        }

        protected override object CreateBehavior()
        {
            return new ErrorHandler();
        }

    }
}

Services/Behaviors/SilverlightFaultBehavior.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace CouponJoe.Services.Behaviors
{
    public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
    {

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
        }

        public class SilverlightFaultMessageInspector : IDispatchMessageInspector
        {
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                if (reply.IsFault)
                {
                    HttpResponseMessageProperty property = new HttpResponseMessageProperty();
                    // Here the response code is changed to 200.
                    property.StatusCode = System.Net.HttpStatusCode.OK;
                    reply.Properties[HttpResponseMessageProperty.Name] = property;
                }
            }

            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                // Do nothing to the incoming message.
                return null;
            }
        }

        // The following methods are stubs and not relevant.

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        public override System.Type BehaviorType
        {
            get { return typeof(SilverlightFaultBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new SilverlightFaultBehavior();
        }

    }

}

Services/Web.config:


<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="ExceptionHandlingBehavior"
             type="CouponJoe.Services.Behaviors.ErrorHandlerBehavior, CouponJoe.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        <add name="SilverlightFaultBehavior"
             type="CouponJoe.Services.Behaviors.SilverlightFaultBehavior, CouponJoe.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <basicHttpBinding>
        <binding name="basicBinding">
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="CouponJoe.Services.ServiceBehavior" name="CouponJoe.Services.CategoryService">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicBinding"
                  behaviorConfiguration="SilverlightFaultEndPointBehavior" contract="CouponJoe.Services.Interfaces.ICategoryService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="SilverlightFaultEndPointBehavior">
          <SilverlightFaultBehavior/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="CouponJoe.Services.ServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <ExceptionHandlingBehavior/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

We're doing 2 things here... First, we're adding an Exception Handling behavior into the WCF pipeline (using ErrorHandler.cs and ErrorHandlerBehavior.cs), which will pick up all Exceptions thrown in the WCF services. We can then package them up in FaultExceptions and send them down to the client. You'll see in the web.config how we plugged it in.

For a normal client this would be the end of it, but Silverlight is "special". Silverlight doesn't grab the true exception because when the service faults, it returns a message other than 200 ("OK"). So the second step is plugging in the Silverlight Fault behavior into the WCF pipeline, which modifies the message to still be 200 ("OK") even when an exception was thrown.

So now that we're getting a nice fault exception passed to SL, what do we do with it? If we're using SL2, the answer is "not much". SL2 has half-baked FaultException calsses that aren't much good. However, SL3 has resolved this. Yeah, but SL3 is beta, right? Yes it is, but there's a delivery date of July 10th so you may be able to rationalize upgrading.

After upgrading to SL3, you can then handle your exceptions globally in the code-behind of App.xaml:

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            //what kind of exception have we caught?
            string errorMessage = String.Empty;
            if (e.ExceptionObject.InnerException != null && e.ExceptionObject.InnerException is FaultException)
            {//wcf exception.
                FaultException exc = e.ExceptionObject.InnerException as FaultException;
                errorMessage = exc.Reason.ToString();
            }
            else
            {//silverlight exception.
                errorMessage = e.ExceptionObject.Message;
                //TODO: log exception.
            }
            //handle exception so app doesn't crash.
            e.Handled = true;
            //show js error.
            Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
            //redirect to error page.
            Navigation.Navigate(Pages.ERROR, errorMessage);
        }

We have accomplished a few things here. You now have global exception handling in both WCF and Silverlight rather than having to add try/catch blocks around everything. You are receiving the true WCF error message from your Silverlight app rather thangeneric communication faults, which can really help debugging, as otherwise you'll most likely have to debug the service while running your Silverlight app in order to figure out what's going on. 

Note: please don't do this in a production app, this is only useful for debugging purposes...



Silverlight + WCF + Entity Framework

clock May 29, 2009 20:13 by author Justin Toth

For an app I'm building, I set up the following architecture:

 

Front-end:

Silverlight project

nUnit test project

Middle-tier:

WCF Services project

Schemas project

SchemasSL project

DataAccess project

Back-end:

Stored procedures / SQL Server database

 

Pretty standard stuff, however there's one odd ball in that list, the SchemasSL project. What is this for, you ask?? Try adding more than 1 WCF service reference to your Silverlight project and you'll then enter into what I call "WCF Service Proxy Namespace Hell", or WSPNH for short (it'll catch on, just give it a couple years...) Each service proxy will contain a set of your business objects, each inconveniently located in that service proxy's namespace. Try to cast Service1Proxy.BusinessObjectA to Service2Proxy.BusinessObjectA and then make yourself feel comfortable in WSPNH while the invalid cast build errors pop up.

The solution is to add a reference to your schemas in the Silverlight app and then configure your service references to reuse referenced assemblies by right-clicking on them and doing Configure Service Reference. Easy enough... not! When you try to add the Schemas reference to your Silverlight app, you'll get an error message about how you can't reference a non-Silverlight project in your Silverlight project. F#$%!! This is where SchemasSL comes in. This is simply a Silverlight class library that references all the business objects in the standard Schemas project as links. You can do this by adding existing items, selecting the schema classes, then hitting the down arrow next to the add button and doing "Add as Link".

Now that we've added a reference to our Schemas, we can go ahead and do Update Service References and now all our schemas in our Silverlight app conveniently are located in the Schemas namespace rather than being schismed in the separate service proxy namespaces. At this point you acknowledge that you've just done a little hack but everything is working so you can sleep tonight.

We're done here, this article is over...

Oh wait, I did mention the Entity Framework in the title, didn't I??

I decided that it'd be a good learning experience to try using the Entity Framework/Linq to Entities. Basically you can add new item -> ADO.NET Entity Date Model, point to a database, tell it to add all tables, and it'll auto-generate schemas for you as well as data access methods with a few clicks. It does much more than this, but that's what I planned on using. Below you can see what my entities looked like... It seems simple and fast and usually it is, but I had named all my tables with the convention TABLE_NAME and all my columns with the convention column_name, so all the entity schemas were generated in the same fashion, which looked nasty in C#. I ended up rewriting all the table and column names to conform to C# standards, not something everyone can do, but if you're ditching all your sprocs then it makes it easier.

With all this auto-generated Entity Framework model code, I no longer needed my Schemas project, my DataAccess project, or my stored procedures (you will want to keep some sprocs for complex logic probably but I didn't have any.) By the way, if you run profiler when calling these Entity Framework methods, you'll see that it generates not just dynamic sql, but dirty sql (dynamic sql outside of sprocs.) This made me throw up a little in my mouth but then I read a bunch of articles where it seems that the sproc performance benefits that I had always believed were there may not be vs. dynamic sql. This seems to be due to the fact that dynamic sql can figure out the best execution pattern each run vs the default behavior of sprocs to figure it out only when they're created. If you want to use all sprocs with the Entity Framework you can do so, but for me, it seems the benefits (not having to write hundreds of sprocs and getting to smeer dirty sql in DBA's faces) by far outweigh the minuses (arguable performance gains, security enhancements that may or may not be applicable).

I then reworked my WCF services to work with the Entity Framework, an example service below:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using CouponJoe.Services.Models;
using System.Data;

namespace MyNameSpace.Services
{
    public class CategoryService : Interfaces.ICategoryService
    {
        public Category Add(Category category)
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //set dates.
                category.CreateDate = DateTime.Now;
                category.ModifiedDate = DateTime.Now;
                //save category.
                dataContext.AddToCategory(category);
                dataContext.SaveChanges();
                //return category.
                return category;
            }
        }

        public Category Update(Category category)
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //set date.
                category.ModifiedDate = DateTime.Now;
                //apply changes.
                EntityKey key = dataContext.CreateEntityKey("Category", category);
                object originalCategory;
                if (dataContext.TryGetObjectByKey(key, out originalCategory))
                {
                    dataContext.ApplyPropertyChanges(key.EntitySetName, category);
                }
                //save category.
                dataContext.SaveChanges();
                //return category.
                return category;
            }
        }

        public int Delete(Category category)
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //delete category.
                dataContext.Attach(category);
                dataContext.DeleteObject(category);
                int numRowsAffected = dataContext.SaveChanges();
                if (numRowsAffected > 0)
                {//return deleted categoryid.
                    return category.CategoryId;
                }
                else
                {//delete didn't work.
                    return -1;
                }
            }
        }

        public Category Get(int categoryId)
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //get category by id.
                IQueryable<Category> categoryQuery =
                    from c in dataContext.Category
                    where c.CategoryId == categoryId
                    select c;
                //return category.
                return categoryQuery.FirstOrDefault();
            }
        }

        public Category GetByName(string categoryName)
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //get category by name.
                IQueryable<Category> categoryQuery =
                    from c in dataContext.Category
                    where c.CategoryName == categoryName
                    select c;
                //return category.
                return categoryQuery.FirstOrDefault();
            }
        }

        public List<Category> Search()
        {
            using (CouponEntities dataContext = new CouponEntities())
            {
                //return categorys.
                return dataContext.Category.ToList();
            }
        }

    }
}

You can see some fun stuff going on with data access and linq but that's not why we're here. First thing I did was try out the new WCF service methods by running some nunit test cases that I had written and worked with the old code. I removed the reference to the Schemas project in my test project, added a reference to the Services project, then updated the service references (to get the services to use my new Entity Framework business schemas rather than my old custom schemas). This worked and I got every test case passing. At this point I thought I was money....

The next step was to actually use the new WCF service methods in my Silverlight app... and this is where we hit a brick wall... You want to add a reference to your new entity framework schemas to your Silverlight project. You can't add a reference directly to your WCF services project because it's not a Silverlight project. I tried using the same hack as I did before, linking to the Entity Framework Model file (ModelName.edmx) from my SchemasSL project then referencing this in my Silverlight app. Unfortunately, you'll get hundreds of build errors in your SchemasSL project about missing references, including System.Data. Sadly you can't add a reference to it, as it's not a Silverlight reference!! I almost gave up at this point but decided to give something else a try, updating the SchemasSL.csproj file in notepad, and adding the references directly in there to get around the Visual Studio validation.

Here is what I added to the references block of mine:

<Reference Include="mscorlib, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll</HintPath>
    </Reference>
    <Reference Include="System, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll</HintPath>
    </Reference>
    <Reference Include="System.Core, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll</HintPath>
    </Reference>
    <Reference Include="System.Data, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll</HintPath>
    </Reference>
    <Reference Include="System.Data.Entity, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.Entity.dll</HintPath>
    </Reference>
    <Reference Include="System.Xml, Version=2.0.50727.3053, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll</HintPath>
    </Reference>

All of these references were required by the Entity Framework generated model classes, and I had to go through one by one until I finally got the SchemasSL project building. I was burnt out at this point but felt like I had finally gotten where I needed to be, that this was going to work. I referenced the SchemasSL project in my Silverlight project, updated the service references, and voalla, everything worked.. Wait no, that was the happy ending I was hoping for.. Instead, the WCF service proxy classes pooped out (that's the technical term for it), they just didn't generate anything... All of the expected classes for ServiceClients and Async Method calls were just absent from the proxy class. Here's an example proxy class:

 

 namespace MyNameSpace.SL.CarrierServiceProxy {
    using System.Runtime.Serialization;
   
   
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="StructuralObject", Namespace="//schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses">http://schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses", IsReference=true)]
    [System.Runtime.Serialization.KnownTypeAttribute(typeof(CouponJoe.SL.CarrierServiceProxy.EntityObject))]
    public partial class StructuralObject {
    }
   
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    public class EntityObject {
    }
}

If I went into Configure Service Reference, selected Reuse types in specified referenced assemblies, and checked all except my SchemasSL project, and then it generated the proxy class ok, except as expected it created the schemas under the service proxy namespace. Yell  That made it clear that even though I tricked it into allowing my non-Silverlight references in my Silverlight class library, when generating the proxies it must care about that and that's what caused it to fail. Of course it doesn't give any error messages though in the error or output windows.

So that's where I'm at, still no solution to get this working... Seems like a simple thing to want to use Silverlight, WCF, and the Entity Framework. I mean, 1 company designed all 3, didn't it? Shouldn't they play nice with each other? I went through all this pain and still it isn't working, what gives???

UPDATE:

I removed the reference to my SchemasSL silverlight class library in my Silverlight app, regenerated the service references (so that they'd generate correctly), then manually removed the extra schemas classes from each reference. This isn't something I'd want to do every time I update or add a new service reference but I had come so far that I wanted to follow through and see if I could get this working. After cleaning up the service proxy classes, I ended up getting hundreds of build errors in my Silverlight app about how there are conflicting references. This occurs because my SchemasSL silverlight class library has references to "System" (.NET's System project), whereas my Silverlight app has a reference to "system" (Silverlight's subset System project). This was a clear sign that there was nothing left to do but turn around and go back...

I revived my custom Schemas project and modified my WCF services to expose only those custom schemas rather than the Entity Framework schemas. So for example, when calling an Add method, it would convert the custom schema business object to an Entity Framework schema then save it. When calling a get method, it would use linq to get an Entity Framework schema object, then would conver to a custom schema business object to be returned from the method. Then I could go back to my old hack of linking my customer schema classes to SchemasSL and referencing that within my Silverlight app.

This worked but isn't as nice and clean as I was hoping for, as you end up having two sets of business objects, 1 within your custom schema project and 1 within the generated Entity Framework model code. So it seems at least for now you should expose custom schema objects from your WCF methods and leave Silverlight apps ignorant that the Entity Framework exists, but one can dream for improvements on this in the future...



Silverlight 2 Navigation

clock May 18, 2009 00:51 by author Justin Toth

I've been working on a Silverlight 2 application for the past week and a half and having started my Silverlight experience with SL3, I took for granted the nice navigation features. With SL2 I was forced to create my own navigation framework, which turned out to be a pain in the butt. I read some solutions on the web but they either weren't nice and clean or they assumed that you wanted to navigate from page to page without having a "master" page. It was at this point that I decided to take a crack at it myself...

The first thing I did was create my Page.xaml, which is my master page. In here I created a StackPanel to hold the usercontrol of the current "page" that the user is on. Alternately instead of dynamically adding usercontrol objects to the StackPanel, you could create a UserControl and set its Content property to the user control of the current page.


 <StackPanel x:Name="spContent" Orientation="Vertical" Margin="10,10,10,10" HorizontalAlignment="Left" />

The next thing I had to do was create a Navigate method in the code-behind of Page.xaml:

 public void Navigate(UIElement page)
        {
            //clear old page.
            spContent.Children.Clear();
            //add new page.
            spContent.Children.Add(page);
        }

Next I created a class called Pages.cs which holds the name and object type of each of our pages. Notice the difference between the HOME_PAGE property and the REGISTER and LOGIN properties. I realized early on that if I declared all of the page properties in the fashion of HOME_PAGE, it would create only one instance and then use that one from then on. The web is stateless but switching between pages in Silverlight is not if you use this method, so if you go to the Register page and fill out the form then go to another page and then back again to Register, you expect the form to be blank again but it won't be. The REGISTER and LOGIN properties will create new instances each time the page is requested to avoid this issue.

public class Pages
    {    
        //public pages.
        public static UserControl HOME_PAGE = new Home();
        public static UserControl REGISTER
        {
            get { return new Register(); }
        }
        public static UserControl LOGIN
        {
            get { return new Login(); }
        }
}

Next I created another class called Navigation.cs, which gives us a nice clean 1-liner way to navigate to a new page. The first thing you'll notice is BasePage, which will be set to the instance of our master page. This gives us a way to call the Navigate method in the master page from here so that in our pages we'll be able to call Navigation.Navigate(myPage); The alternative is referencing the App object, then referencing the master page (App.RootVisual), then calling Navigate from there (ugly!)

We have an overloaded Navigate method that lets us pass in a State object. This is just a way to pass a parameter to the next page you go to. A common use would be if you are on a business object list page and you're going to an edit business object page, you could pass the business object in here and then use it to populate the edit form in the edit page without having to hit the database again (very cool!)

The last thing you'll notice is that we're saving the name of the current page we're on in Isolated Storage, which you can think of as Silverlight's version of a cookie. It gets saved on the end-user's computer and gives us a way to persist the value if they exit our application. An important thing to remember here is that even hitting the browser's refresh will exit our application, which by default would bring them back to our default home page. By saving the page name, we can then bring the user back to the page they were on before they hit refresh.

using System.IO.IsolatedStorage;

public static class Navigation
    {
        //properties.
        public static Page BasePage { get; set; }
        public static object State { get; set; }

        public static void Navigate(UserControl page)
        {
            //erase state.
            State = null;
            //navigate.
            NavigateTo(page);
        }

        public static void Navigate(UserControl page, object state)
        {
            //save state to pass between pages.
            State = state;
            //navigate.
            NavigateTo(page);
        }

        private static void NavigateTo(UserControl page)
        {
            //save current page.
            SavePersistentValue("Current Page", page.ToString());
            //navigate.
            BasePage.Navigate(page);
        }

        #region Isolated Storage

        public static object GetPersistentValue(string key)
        {
            IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
            if (appSettings.Contains(key))
            {
                return appSettings[key] as object;
            }
            else
            {
                return null;
            }
        }

        public static void SavePersistentValue(string key, object value)
        {
            IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
            if (appSettings.Contains(key))
            {
                appSettings[key] = value;
            }
            else
            {
                appSettings.Add(key, value);
            }
        }

        #endregion

The last step is to handle the Application_Startup event in App.xaml.cs to set our Navigation.BasePage to our master page and then load up the current page we were on before the user exited the application.

private void Application_Startup(object sender, StartupEventArgs e)
        {
            //navigate to home page.
            this.RootVisual = new Page();
            Navigation.BasePage = this.RootVisual as Page;
            //check which page we were on.
            string pageName = Convert.ToString(Navigation.GetPersistentValue("Current Page"));
            if (pageName.Length > 0)
            {//navigate to current page.
                Type pageType = Type.GetType(pageName);
                UserControl currentPage = Activator.CreateInstance(pageType) as UserControl;
                Navigation.Navigate(currentPage);
            }
        }

And that's all folks... If you wanted to switch to the login page you would just do Navigation.Navigate(Pages.LOGIN), nice and clean...



Developing for Silverlight 2 and Silverlight 3

clock May 13, 2009 16:42 by author Justin Toth

I've been developing my personal website in Silverlight 3 beta as a way to learn the new features. However, today I needed to begin development on a website that will be going live fairly soon. As Silverlight 3 isn't projected to be released until maybe July or August (and it could very well be even later), I was forced to choose Silverlight 2. Unfortunately, you can't develop for Silverlight 2 and Silverlight 3 at the same time on the same box. As I'm opposed to using VM's these days after losing about a year of my life due to slow VM performance in the past, I searched google and was lucky enough to find this article:

http://blogs.msdn.com/amyd/archive/2009/03/18/switching-from-silverlight-3-tools-to-silverlight-2-tools.aspx

It gives you a nice .bat script that will automate most of the process of going between SL2 and SL3 so that you can easily switch between the 2 by clicking a few Next buttons and waiting 1-2 minutes. Problem solved!



Silverlight 3 ChildWindow Modal Popup

clock May 9, 2009 23:18 by author Justin Toth

Tonight I was looking at the Silverlight Toolkit Samples and found the ChildWindow control. This interested me because the first Silverlight tutorial I ever took was ScottGu's on building an application that can search for Digg articles, found here. When you click on an article in the listbox then a modal popup comes up containing more information. He achieved this modal popup functionality by creating a grey rectangle with some opacity that stretched across the screen horizontally and vertically. On top of that was a rounded border control with the content in there that would show up over the rectangle.

I used the concept from ScottGu's tutorial to create a page on my personal website that has a ListBox of some websites that I've created. When you click on one, the modal popup comes up showing more details of the work I did for that website. The ChildWindow control seemed a bit more polished to me so I decided to give it a go.

The first thing you need to do is make sure your project contains a reference to System.Windows.Controls. Interestingly, my application was a Silverlight Navigation project and already contained references to System.Windows.Controls and System.Windows.Controls.Navigation, which made me realize that the navigation functionality that I had been using must be a part of the Silverlight Toolkit! The next thing you'll want to do is add a control to your project for the modal popup. The cool thing here is that there is already a "Silverlight Child Window" control that you can add:

Here's what I did with my child window control, notice how I don't need to worry about the formatting of the window or the modal functionality, all I need to do is add my content that will go inside the modal popup.


<controls:ChildWindow x:Class="TothSolutions.SL.Controls.WorkPopup"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
           Width="600" Height="300"
           Title="WorkPopup">
    <Grid x:Name="LayoutRoot" Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
       
        <Image Source="{Binding ThumbNailBig}" Width="250" Margin="0,0,20,0" VerticalAlignment="Center" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3" />
        <TextBlock Text="{Binding Title}" FontSize="20" Foreground="#000" FontWeight="Bold" Grid.Column="1" Grid.Row="0" />
        <HyperlinkButton Content="{Binding HrefLink}" TargetName="_new" NavigateUri="{Binding HrefLink}" FontSize="12" Margin="0,0,0,5" Grid.Column="1" Grid.Row="1" IsTabStop="False" />
        <TextBlock Text="{Binding FullDescription}" FontSize="12" Foreground="#000;" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" />
        <Button x:Name="CancelButton" Content="Close" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Center" Margin="0,12,0,0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" />
    </Grid>
</controls:ChildWindow>

In the code-behind of the ChildWindow I wait until the control is loaded and then set the title of the modal popup based on the DataContext, which we'll pass in from the parent page. The title as well as a close button will show up at the top of the modal popup without us having to do anything more... Note: you could set the title in the constructor if it's a static value, e.g. this.Title = "My super cool modal popup!";

    public partial class WorkPopup : ChildWindow
    {
        public WorkPopup()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(WorkPopup_Loaded);
        }

        void WorkPopup_Loaded(object sender, RoutedEventArgs e)
        {
            Site site = DataContext as Site;
            this.Title = site.Title;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }
    }

Lastly, on the parent page when the user clicks one of the items in my listbox, I create an instance of my ChildWindow, grab the business object for that row, set that business object as the DataContext of my ChildWindow, and lastly show the ChildWindow.

 private void SiteList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (SiteList.SelectedItem != null)
            {
                Site site = SiteList.SelectedItem as Site;
                WorkPopup popup = new WorkPopup();
                popup.DataContext = site;
                popup.Show();
            }
        }

And that's it, easy peezie... Happy coding!!



Animating Page Transitions in Silverlight 3

clock May 8, 2009 10:31 by author Justin Toth

I wanted to add simple fade out/fade in animations to my page transitions in my Silverlight 3 app. I decided to tackle the fade in animation first, and my first attempt was to add a FadeIn storyboard to the LayoutRoot Grid of each of my pages like so:


        <Grid.Triggers>
            <EventTrigger RoutedEvent="Grid.Loaded">
                <BeginStoryboard>
                    <Storyboard x:Name="FadeIn">
                        <DoubleAnimation Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Opacity"
                From="0" To="1" Duration="0:0:0.300"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Grid.Triggers>
 

You also have to add an Opacity of 0 to your LayoutRoot Grid. There are 2 issues with this approach. 1. You can't work in the designer anymore, as it's now set to invisible. 2. There is now duplicate xaml in every page, if you want to make changes to your animations than you'll have to do it in every single page.

My second attempt was to try and fade in/fade out the frame within MainPage.xaml. I set the frame's opacity to 0. The cool thing is that you can add storyboards to be invoked by code rather than attaching them to individual controls:


<UserControl.Resources>
  <Storyboard x:Name="FadeIn">
   <DoubleAnimation Storyboard.TargetName="Frame" Storyboard.TargetProperty="Opacity"
    From="0" To="1" Duration="0:0:0.200"/>
  </Storyboard>
  <Storyboard x:Name="FadeOut">
   <DoubleAnimation Storyboard.TargetName="Frame" Storyboard.TargetProperty="Opacity"
    From="1" To="0" Duration="0:0:0.200"/>
  </Storyboard>
 </UserControl.Resources> 

Next I had some work to do in the code-behind:

        private Button lnkMenu = null;

        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
            this.FadeOut.Completed += new EventHandler(FadeOut_Completed);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            FadeIn.Begin();
        }

        private void NavButton_Click(object sender, RoutedEventArgs e)
        {
            lnkMenu = sender as Button;
            FadeOut.Begin();
        }

        void FadeOut_Completed(object sender, EventArgs e)
        {
            String page = lnkMenu.Tag.ToString();
            this.Frame.Navigate(new Uri(page, UriKind.Relative));
            FadeIn.Begin();
        }

Now we have all the page transition animation code in one place so we don't have to deal with the maintainability issue of having it in each page. Also the designers for every single page (including MainPage.xaml) will work now that we're not setting the opacity of any of the pages to 0.



Silverlight StackPanel + ListBox Issue

clock May 4, 2009 19:49 by author Justin Toth

Yesterday I was working on a Silverlight 3 beta application which didn't have fixed widths or heights, as I wanted the whole layout to expand and contract as the browser window did. For the layout I used StackPanels, as they seemed to be the most intuitive layout element due to their simple function - laying out items either horizontally or vertically.

Innocently, I created a ListBox that contained a TextBlock that was set to wrap. Unfortunately, the ListBox didn't respect its parent StackPanel container and the text extended off the right side of the page. My short-term hack was to hard-code in a width for the TextBlock but that meant that my ListBox didn't expand and contract as the page did.

I then created a large number of items in the ListBox and the bottom items disappeared off the bottom of the page. I knew that the ListBox internally contained a ScrollViewer so I was surprised why this wasn't invoked when it went off the page, again seeing the ListBox having no respect for its parent StackPanel container.

The solution was to replace the StackPanels that were parents of the ListBox with Grids and to set a property on the ListBox:

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">

This forced the ListBox to stay within the boundaries of the Grid and triggered the ListBox's internal vertical scrollbar to be invoked when it reached the bottom of the page. You can see an example of the listbox behaving itself here.



Introduction

clock May 3, 2009 19:30 by author Justin Toth

This is my first post and I'm excited to start blogging!! It seems like every day I run into some sort of issue and once I figure out how to solve it, it makes me wish I had a blog so I could post the solution so that others won't have to spend as much time on it as I did.

This blog will be related to .NET development, and more specifically front-end web development. I used to consider myself an ASP.NET developer but I feel like we've come upon a crossroads. Straight ahead is ASP.NET, to the left is pure javascript development, and to the right is Silverlight. You look further down the ASP.NET road and see that there's another branch up beyond this one, ASP.NET going to the left and ASP.NET MVC going to the right. Which road to take??



About the author

Justin

Justin is a senior .NET developer who has been working with .NET since 2003. His personal website is located at http://tothsolutions.com.

Sign in