Blake Niemyjski

  • Beginning Silverlight 4 in C#

    I have read many technical books over the years and this is one is one of those books that you know you better keep handy. I really like how Robert has this book laid out and how he introduces you to Silverlight. I’ve been a .NET guy since version 1.0. But like most developers, I haven’t had the chance to really dive into Microsoft’s latest technologies like Silverlight until they have a few versions released.

    I found that the book is laid out very nicely; the first few chapters will introduce you to the tools (Visual Studio 2010). Then it will lead you into what Silverlight is and how to use it. What I really liked about this book is that it gave you background information about previous versions of Silverlight. This is a great way to learn Silverlight as you soon realize the early gotchas of the framework so if you are developing a new Silverlight application or joining a team tasked with upgrading a Silverlight 2.0 application. You will be ready to go.

    Beginning Silverlight 4 will walk you through data binding, layout management, Silverlight controls and much more.  It takes a slightly different but much appreciated way of presenting information to the reader. Each section offers two methods of implementation that contain pictures code samples to solve the presented task; usually declaratively and in code. I like this approach because it allows you to learn the approach that’s fits your coding style.

    I would recommend this book to anyone wanting to learn Silverlight. You should be knowledgeable in C# before picking up this book! My only recommendation for Robert in a second edition is I wish the code that was presented in the book was in color!

    Remember to pick up a copy on Amazon or Apress.

  • How-to: Rename your generated CSLA property names

    In the following example, I'll show you how to use the CodeSmith CSLA template's powerful renaming options. Many of you might have a legacy database with those ugly prefixes, or things you wish you could refactor but just can't. Luckily, you're in luck as CodeSmith is going to save the day once again! By default we will strip any invalid characters from your property names like prefixed digits etc... Please note that these methods also work for renaming class names, but there are other methods that we will cover later that are is the recommended way to specify a class name.

    In this example, I'm going to be renaming column names in the following database table:

    CREATE TABLE [dbo].[Rename](
    	[RenameID] [int] NOT NULL,
    	[RenameName] [nvarchar](50) NOT NULL,
    	[RenameA] [nvarchar](50) NOT NULL,
    	[Name] [nvarchar](50) NOT NULL,
    	[RenameAge] [int] NOT NULL,
     CONSTRAINT [PK_Rename] PRIMARY KEY CLUSTERED 
    (
    	[RenameID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    

    As you can see this would cover a few use cases when it comes to legacy database tables. By default when this is generated the code will look like this. Please note that all code has been simplified to show the results only.

    C#

    public System.Int32 RenameID { get; set; }
    
    public System.String RenameName { get; set; }
    
    public System.String RenameA { get; set; }
    
    public System.String Name { get; set; }
    
    public System.Int32 RenameAge { get; set; }

    Visual Basic

    Public Property RenameID() As System.Int32
    End Property
    
    Public Property RenameName() As System.String
    End Property
    
    Public Property RenameA() As System.String
    End Property
    
    Public Property Name() As System.String
    End Property
    
    Public Property RenameAge() As System.Int32
    End Property

    As you can see the name's haven't changed at all. I'll now show you how to rename the property names! 

    Method 1

    This method can be a lot of work, but for renaming a few columns is the quickest and easiest way to rename property names. Inside of the CSLA Template folder, there will be a folder called Common. Open this folder and double click on the KeywordRenameAlias file. In the future, we would like to make this a per project setting as this method renames everything generated by the templates. To create a per project setting use a different method or create a copy of the templates for a specific project.

    I'm going to show you how to quickly rename the RenameAge column to Age and the RenameA column to Location. As you can see there are already a few existing entries. Well add the two entries and then hit the save icon. Then we will go back and regenerate.

    The generated code will be updated with our new names!

    C#

    public System.Int32 RenameID { get; set; }
    
    public System.String RenameName { get; set; }
    
    public System.String Location{ get; set; }
    
    public System.String Name { get; set; }
    
    public System.Int32 Age { get; set; }

    Visual Basic

    Public Property RenameID() As System.Int32
    End Property
    
    Public Property RenameName() As System.String
    End Property
    
    Public Property Location() As System.String
    End Property
    
    Public Property Name() As System.String
    End Property
    
    Public Property Age() As System.Int32
    End Property
    We will now revert the KeywordRenameAlias mapping file back to it's default values.

    Method 2

    This method works much like Method one, instead your settings are stored as extended properties inside of SQL Server. We will open SQL Management Studio and right click on the column in question and select properties. Then we will add an extended property called CS_Alias with the value being the desired name.

    SQL

    EXEC sys.sp_addextendedproperty @name=N'CS_Alias', @value=N'Location' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Rename', @level2type=N'COLUMN',@level2name=N'RenameA'
    GO
    

    Method 3

    This method is very powerful and could save you a bunch of time! However, it currently requires you to open up the source solution located in the Source folder, make one change and recompile. We will make this much easier to do in a later version. Inside of the CodeSmith.SchemaHelper Project, open up the Configuration class. Then find the following line:

    NamingProperty = new NamingProperty
        {
            EntityNaming = EntityNaming.Singular,
            TableNaming = TableNaming.Mixed,
            ColumnNaming = ColumnNaming.Preserve
        };

    You will want to change the ColumnNaming value to this:

    NamingProperty = new NamingProperty
        {
            EntityNaming = EntityNaming.Singular,
            TableNaming = TableNaming.Mixed,
            ColumnNaming = ColumnNaming.RemoveTablePrefix
        };

    When ColumnNaming is set to RemoveTablePrefix, sit back and be amazed at how much cleaner your properties become automatically! When this is set, Method 1 and Method 2 will run and take preference. After Method 1 or Method 2 run, if the property name matches the cleaned table name and the renaming character length is greater than one character(for uniqueness) then we will strip the table name from the property name, then rerun Method 1 and Method 2. Please note: any association names will not be renamed via this method.

    After regenerating, your properties should look something like this:

    C#

    public System.Int32 Identification { get; set; }
    
    public System.String Name1 { get; set; }
    
    public System.String RenameA { get; set; }
    
    public System.String Name2 { get; set; }
    
    public System.Int32 Age { get; set; }

    Visual Basic

    Public Property Identification() As System.Int32
    End Property
    
    Public Property Name1() As System.String
    End Property
    
    Public Property RenameA() As System.String
    End Property
    
    Public Property Name2 () As System.String
    End Property
    
    Public Property Age() As System.Int32
    End Property
    You might be wondering why RenameID was changed into Identification. This is because the table prefix was stripped leaving ID, ID is predefined in Method 1 by default, and thus was renamed to Identification. As you can see if your column name is RenameA, then you will need to use Method 1 or Method 2 as we won't rename anything with this method that would result in a really short name.
    Don't worry, we didn't change the generated stored procedures or parameterized Sql column names because of these methods!!!

    C#

    const string commandText = "INSERT INTO [dbo].[Rename] ([RenameID], [RenameName], [RenameA], [Name], [RenameAge]) VALUES (@p_RenameID, @p_RenameName, @p_RenameA, @p_Name, @p_RenameAge)";
    using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
    {
        connection.Open();
        using(SqlCommand command = new SqlCommand(commandText, connection))
        {
            command.Parameters.AddWithValue("@p_RenameID", this.Identification);
    		command.Parameters.AddWithValue("@p_RenameName", this.Name1);
    		command.Parameters.AddWithValue("@p_RenameA", this.RenameA);
    		command.Parameters.AddWithValue("@p_Name", this.Name2);
    		command.Parameters.AddWithValue("@p_RenameAge", this.Age);
    
            //result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed. 
            int result = command.ExecuteNonQuery();
            if (result == 0)
                throw new DBConcurrencyException("The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.");
    
            LoadProperty(_originalIdentificationProperty, this.Identification);
        }
    }

    Visual Basic

    Const commandText As String = "INSERT INTO [dbo].[Rename] ([RenameID], [RenameName], [RenameA], [Name], [RenameAge]) VALUES (@p_RenameID, @p_RenameName, @p_RenameA, @p_Name, @p_RenameAge)"
    Using connection As New SqlConnection(ADOHelper.ConnectionString)
        connection.Open()
        Using command As New SqlCommand(commandText, connection)
            command.Parameters.AddWithValue("@p_RenameID", Me.Identification)
    	command.Parameters.AddWithValue("@p_RenameName", Me.Name1)
    	command.Parameters.AddWithValue("@p_RenameA", Me.RenameA)
    	command.Parameters.AddWithValue("@p_Name", Me.Name2)
    	command.Parameters.AddWithValue("@p_RenameAge", Me.Age)
        
            'result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed. 
            Dim result As Integer = command.ExecuteNonQuery()
            If (result = 0) Then
                throw new DBConcurrencyException("The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.")
            End If
        End Using
        
        LoadProperty(_originalIdentificationProperty, Me.Identification)
        
    End Using
  • How-to: Using a Stored Procedure extended property to preserve custom logic in the CSLA generated code.

    In the previous article on how to use the partial methods in the CodeSmith CSLA templates to preserve custom code mostly focused on the code side of things. For those of you who might be using Stored Procedures might be wondering... How do I accomplish preserving my custom stored procedure logic? Don't worry, CodeSmith already though of this and didn't leave you out in the dark! The CSLA templates by default will not automatically execute the generated stored procedures but it can! To do this you need to set the AutoExecuteStoredProcedures property to True. Now, when your database related metadata changes so will your stored procedures.

    This is a great feature until you need to implement custom logic into your stored procedure. By default we will generate logic that will prevent a stored procedure from being overwritten if you have AutoExecuteStoredProcedures turned to true or you try to drop the stored procedure. Here is the example that we are going to be modifying that will result in the same exact output as our previous sample.

    SET NUMERIC_ROUNDABORT OFF
    GO
    SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
    GO
    IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
    GO
    CREATE TABLE #tmpErrors (Error int)
    GO
    SET XACT_ABORT ON
    GO
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    GO
    BEGIN TRANSACTION
    GO
    PRINT N'Dropping CSLA_Calendar_Select'
    GO
    IF EXISTS(SELECT 1 FROM fn_listextendedproperty (NULL, 'SCHEMA', 'dbo', 'PROCEDURE', 'CSLA_Calendar_Select', default, default) WHERE name = 'CustomProcedure' and value = '1')
    BEGIN
        RAISERROR ('The procedure CSLA_Calendar_Select has an Extended Property "CustomProcedure" which means is has been customized. Please review and remove the property if you wish to drop the procedure.',16,1)
        INSERT INTO #tmpErrors (Error) SELECT 1
    END
    GO
    
    IF OBJECT_ID(N'CSLA_Calendar_Select') IS NOT NULL
    	DROP PROCEDURE CSLA_Calendar_Select
    
    GO
    IF @@ERROR!=0 AND @@TRANCOUNT>0 ROLLBACK TRANSACTION
    GO
    
    IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
    GO
    
    IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
    GO
    
    IF @@TRANCOUNT>0 BEGIN
    PRINT 'The stored procedure drop has succeeded'
    COMMIT TRANSACTION
    END
    ELSE PRINT 'The stored procedure drop has failed'
    GO
    
    DROP TABLE #tmpErrors
    GO
    
    --region [dbo].[CSLA_Calendar_Select]
    
    ------------------------------------------------------------------------------------------------------------------------
    -- Generated By:   Blake Niemyjski using CodeSmith: v5.2.3, CSLA Templates: v3.0.0.1888, CSLA Framework: v3.8.4
    -- Procedure Name: [dbo].[CSLA_Calendar_Select]
    ------------------------------------------------------------------------------------------------------------------------
    
    SET NUMERIC_ROUNDABORT OFF
    GO
    SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
    GO
    IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
    GO
    CREATE TABLE #tmpErrors (Error int)
    GO
    SET XACT_ABORT ON
    GO
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    GO
    BEGIN TRANSACTION
    GO
    
    PRINT N'Creating [dbo].[CSLA_Calendar_Select]'
    GO
    
    IF EXISTS(SELECT 1 FROM fn_listextendedproperty (NULL, 'SCHEMA', 'dbo', 'PROCEDURE', 'CSLA_Calendar_Select', default, default) WHERE name = 'CustomProcedure' and value = '1')
        BEGIN
            RAISERROR ('The procedure [dbo].[CSLA_Calendar_Select] has an Extended Property "CustomProcedure" which means is has been customized. Please review and remove the property if you wish to create the stored procedure.',16,1)
            INSERT INTO #tmpErrors (Error) SELECT 1
        END
    GO
    
    CREATE PROCEDURE [dbo].[CSLA_Calendar_Select]
    	@p_ID nvarchar(50) = NULL,
    	@p_CalendarName nvarchar(50) = NULL,
    	@p_Name nvarchar(50) = NULL,
    	@p_EventStart datetime = NULL,
    	@p_EventEnd datetime = NULL,
    	@p_Resource nvarchar(150) = NULL
    AS
    
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    
    SELECT
    	[ID],
    	[CalendarName],
    	[Name],
    	[EventStart],
    	[EventEnd],
    	[Resource]
    FROM
        [dbo].[Calendar]
    WHERE
    	([ID] = @p_ID OR @p_ID IS NULL)
    	AND ([CalendarName] = @p_CalendarName OR @p_CalendarName IS NULL)
    	AND ([Name] = @p_Name OR @p_Name IS NULL)
    	AND ([EventStart] = @p_EventStart OR @p_EventStart IS NULL)
    	AND ([EventEnd] = @p_EventEnd OR @p_EventEnd IS NULL)
    	AND ([Resource] = @p_Resource OR @p_Resource IS NULL)
    
    GO
    IF @@ERROR!=0 AND @@TRANCOUNT>0 ROLLBACK TRANSACTION
    GO
    
    IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
    GO
    
    IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
    GO
    IF @@TRANCOUNT>0 BEGIN
    PRINT 'Stored procedure creation succeeded.'
    COMMIT TRANSACTION
    END
    ELSE PRINT 'Stored procedure creation failed.'
    GO
    DROP TABLE #tmpErrors
    GO
    
    --endregion

    Let's open up SQL Management Studio and run the script above. Once you run the query above, you can see that the table was dropped and recreated.

    Dropping CSLA_Calendar_Select

    The stored procedure drop has succeeded

    Creating [dbo].[CSLA_Calendar_Select]

    Stored procedure creation succeeded.

    Now that you can see that the stored procedure is being overwritten, let's modify the stored procedure to return the same results as our previous example.

    ALTER PROCEDURE [dbo].[CSLA_Calendar_Select]
    	@p_ID nvarchar(50) = NULL,
    	@p_CalendarName nvarchar(50) = NULL,
    	@p_Name nvarchar(50) = NULL,
    	@p_EventStart datetime = NULL,
    	@p_EventEnd datetime = NULL,
    	@p_Resource nvarchar(150) = NULL
    AS
    
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    
    SELECT
    	[ID],
    	[CalendarName],
    	[Name],
    	[EventStart],
    	[EventEnd],
    	[Resource]
    FROM
        [dbo].[Calendar]
    WHERE
    	([ID] = @p_ID OR @p_ID IS NULL)
    	AND ([CalendarName] = @p_CalendarName OR @p_CalendarName IS NULL)
    	AND ([Name] = @p_Name OR @p_Name IS NULL)
    	AND ([EventStart] = @p_EventStart OR @p_EventStart IS NULL)
    	AND ([EventEnd] = @p_EventEnd OR @p_EventEnd IS NULL)
    	AND ([Resource] = @p_Resource OR @p_Resource IS NULL)
    	AND [EventStart] >= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)

    Since you modified the stored procedure, you want to mark it as a custom Stored Procedure. If you choose not to do this, then you will need to modify this stored procedure any time you wish to regenerate or drop and recreate this stored procedure. This can be done by right clicking on the stored procedure, selecting properties and then selecting the Extended Properties node. You will want to create a new extended property with a name of CustomProcedure and a value of 1. The following SQL Script will also create the extended property.

    EXEC sys.sp_addextendedproperty @name=N'CustomProcedure', @value=N'1' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'CSLA_Calendar_Select'
    GO

    Now when you execute the SQL script to drop and create the stored procedure it will error out and preserve your changes!

    Dropping CSLA_Calendar_Select

    Msg 50000, Level 16, State 1, Line 3

    The procedure CSLA_Calendar_Select has an Extended Property "CustomProcedure" which means is has been customized. Please review and remove the property if you wish to drop the procedure.


    (1 row(s) affected)

    The stored procedure drop has failed

    Creating [dbo].[CSLA_Calendar_Select]

    Msg 50000, Level 16, State 1, Line 4

    The procedure [dbo].[CSLA_Calendar_Select] has an Extended Property "CustomProcedure" which means is has been customized. Please review and remove the property if you wish to create the stored procedure.


    (1 row(s) affected)

    Msg 2714, Level 16, State 3, Procedure CSLA_Calendar_Select, Line 13

    There is already an object named 'CSLA_Calendar_Select' in the database.


    (1 row(s) affected)

    Stored procedure creation failed.

     

  • How-to: Using the CSLA partial methods to preserve custom code and promote active regeneration.

    One of the best features of code generation is the reality of being able to actively generate you code while preserving custom changes. This allows you to implement custom logic while constantly making changes to your metadata and getting the latest template changes. CodeSmith has worked hard on making this happen in both the PLINQO and CSLA Templates. The CSLA templates offer many partial method overrides to make your life easier. It is recommended that you place any partial methods that you implement in the non-generated partial class; this is always the ClassName.cs or ClassName.vb source code file.

    A list of partial methods can be found at the bottom of the every generated partial class. Here is an example of the generated partial methods.

    C#

    #region DataPortal partial methods
    
    partial void OnCreating(ref bool cancel);
    partial void OnCreated();
    partial void OnFetching(CalendarCriteria criteria, ref bool cancel);
    partial void OnFetched();
    partial void OnMapping(SafeDataReader reader, ref bool cancel);
    partial void OnMapped();
    partial void OnUpdating(ref bool cancel);
    partial void OnUpdated();
    partial void OnAddNewCore(ref Calendar item, ref bool cancel);
    
    #endregion

    Visual Basic

    #Region "DataPortal partial methods"
        
    Partial Private Sub OnCreating(ByRef cancel As Boolean)
    End Sub
    Partial Private Sub OnCreated()
    End Sub
    Partial Private Sub OnFetching(ByVal criteria As CalendarCriteria, ByRef cancel As Boolean)
    End Sub
    Partial Private Sub OnFetched()
    End Sub
    Partial Private Sub OnMapping(ByVal reader As SafeDataReader, ByRef cancel As Boolean)
    End Sub
    Partial Private Sub OnMapped()
    End Sub
    Partial Private Sub OnUpdating(ByRef cancel As Boolean)
    End Sub
    Partial Private Sub OnUpdated()
    End Sub
    Partial Private Sub OnAddNewCore(ByVal item As Calendar, ByRef cancel As Boolean)
    End Sub
        
    #End Region

    In this example we will take an EditableRootList called CalendarList, whose purpose is to hold a list of Calendar business objects. The default generated code will select all calendar entries with a specific criterion. The code below is the code that is generated in the CalendarList.DataAccess partial class:

    C#

    private void DataPortal_Fetch(CalendarCriteria criteria)
    {
        bool cancel = false;
        OnFetching(criteria, ref cancel);
        if (cancel) return;
    
        RaiseListChangedEvents = false;
    
        // Fetch Child objects.
        string commandText = string.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}", ADOHelper.BuildWhereStatement(criteria.StateBag));
        using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
        {
            connection.Open();
            using (SqlCommand command = new SqlCommand(commandText, connection))
            {
                command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
    
                using(var reader = new SafeDataReader(command.ExecuteReader()))
                {
                    if(reader.Read())
                    {
                        do
                        {
                            this.Add(new Calendar(reader));
                        } while(reader.Read());
                    }
                }
            }
        }
    
        RaiseListChangedEvents = true;
    
        OnFetched();
    }

    Visual Basic

     

    Private Shadows Sub DataPortal_Fetch(ByVal criteria As CalendarCriteria)
        Dim cancel As Boolean = False
        OnFetching(criteria, cancel)
        If (cancel) Then
            Return
        End If
    
        RaiseListChangedEvents = False
    
        ' Fetch Child objects.
        Dim commandText As String = String.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}", ADOHelper.BuildWhereStatement(criteria.StateBag))
        Using connection As New SqlConnection(ADOHelper.ConnectionString)
            connection.Open()
            Using command As New SqlCommand(commandText, connection)
                command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
                Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                    If reader.Read() Then
                        Do
                            Me.Add(New Calendar(reader))
                        Loop While reader.Read()
                    End If
                End Using
            End Using
        End Using
    
        RaiseListChangedEvents = True
    
        OnFetched()
    End Sub

     

    As you can see that this isn't optimal because we only want to select all Calendar entries from the first of the current month. In the real word we would only want to select maybe a month of two or data... This can easily be done by overriding the partial method and modifying the sql statement. We will now go to the partial non generated class (CalendarList.cs or CalendarList.vb):

    C#

    #region Custom Data Access
    
    partial void OnFetching(CalendarCriteria criteria, ref bool cancel)
    {
        RaiseListChangedEvents = false;
    
        // Fetch Child objects.
        string commandText = string.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] >= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)", ADOHelper.BuildWhereStatement(criteria.StateBag));
        using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
        {
            connection.Open();
            using (SqlCommand command = new SqlCommand(commandText, connection))
            {
                command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
    
                using (var reader = new SafeDataReader(command.ExecuteReader()))
                {
                    if (reader.Read())
                    {
                        do
                        {
                            this.Add(new Calendar(reader));
                        } while (reader.Read());
                    }
                }
            }
        }
    
        RaiseListChangedEvents = true;
    
        // Cancel the existing Dal method.
        cancel = true;
    }
    
    #endregion

    Visual Basic

    #Region "Custom Data Access"
    Private Sub OnFetching(ByVal criteria As CategoryCriteria, ByRef cancel As Boolean)
        RaiseListChangedEvents = False
    
        ' Fetch Child objects.
        Dim commandText As String = String.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] >= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)", ADOHelper.BuildWhereStatement(criteria.StateBag))
        Using connection As New SqlConnection(ADOHelper.ConnectionString)
            connection.Open()
            Using command As New SqlCommand(commandText, connection)
                command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
                Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                    If reader.Read() Then
                        Do
                            Me.Add(New Calendar(reader))
                        Loop While reader.Read()
                    End If
                End Using
            End Using
        End Using
    
        RaiseListChangedEvents = True
    
        'Cancel the existing Dal method.
        cancel = True
    End Sub
    #End Region

    As you can see all we needed to do was implement the OnFetching partial method and override the sql query that was then sent to the database server. You can do anything you want in this partial method. The last thing to do is set the reference cancel variable to true if you wish to have everything after the partial method call ignored. If you set this to false then the existing generated code will also run. In this case, we want to cancel out. A scenario where you would want to return false, is if you wanted to do some processing before accessing the database.

    If you come across something that you wish had a partial method, please log it on the CodeSmith template issue tracker.

  • Breaking CSLA 4.0 property changes when using private backing fields.

    I came across this bug last night, which was occurring right as CSLA4 was released. It had to deal with the CodeSmith CSLA templates when you set UseMemberVariables to true. The error is quite obscure and not very intuitive as the signs of a bug are only found when you check to see if your business object IsValid. The scenario in this case is when you set a required managed property, the rules never fully evaluate the set value. A bunch of other side effects could occur as well. Here is the error you could receive:

    CategoryId required
      Expected: True
      But was:  False

    Here is the code that was causing the error:

    C#

    protected override void AddBusinessRules()
    {
        BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(_categoryIdProperty));
    }
    
    private static readonly PropertyInfo< System.String > _categoryIdProperty = RegisterProperty< System.String >(p => p.CategoryId, string.Empty);
    private System.String _categoryId = _categoryIdProperty.DefaultValue;
    public System.String CategoryId
    {
        get { return GetProperty(_categoryIdProperty, _categoryId); }
        set { SetProperty(_categoryIdProperty, ref _categoryId, value); }
    }
    
    [Test]
    private void CreateCategory(string categoryID)
    {
        Category category = Category.NewCategory();
        category.CategoryId = categoryID;
        Assert.IsTrue(category.IsValid, category.BrokenRulesCollection.ToString());
    
        category = category.Save();
    
        Assert.IsTrue(category.CategoryId == categoryID);
    }

    Visual Basic

    Protected Overrides Sub AddBusinessRules()
        BusinessRules.AddRule(New Csla.Rules.CommonRules.Required(_categoryIdProperty))
    End Sub
    
    Private Shared ReadOnly _categoryIdProperty As PropertyInfo(Of System.String) = RegisterProperty(Of System.String)(Function(p As Category) p.CategoryId, String.Empty)
    Private _categoryId As System.String = _categoryIdProperty.DefaultValue
    Public Property CategoryId() As System.String
        Get 
            Return GetProperty(_categoryIdProperty, _categoryId) 
        End Get
        Set (value As System.String)
            SetProperty(_categoryIdProperty, _categoryId, value)
        End Set
    End Property
    
    <Test()> _
    Private Sub CreateCategory(ByVal categoryID As String)
        Dim category As Category = PetShop.Tests.ParameterizedSQL.Category.NewCategory()
        category.CategoryId = categoryID
    
        Assert.IsTrue(category.IsValid, category.BrokenRulesCollection.ToString())
        category = category.Save()
    
        Assert.IsTrue(category.CategoryId = categoryID)
    End Sub

    As you can see the code looks and works fine in CSLA 3.8 but it isn't the case in 4.0. Once I tracked down what was causing the error, I saw that there wasn't any checks in CSLA4 to see if the property had the RelationshipTypes.PrivateField overload when you are calling the GetProperty or SetProperty methods with a backing field. The long term fix when using private member backing fields is to set the RelationshipTypes to RelationshipTypes.PrivateField.

    C#

    private static readonly PropertyInfo< System.String > _categoryIdProperty = RegisterProperty< System.String >(p => p.CategoryId, string.Empty, RelationshipTypes.PrivateField);
    private System.String _categoryId = _categoryIdProperty.DefaultValue;
    public System.String CategoryId
    {
        get { return GetProperty(_categoryIdProperty, _categoryId); }
        set { SetProperty(_categoryIdProperty, ref _categoryId, value); }
    }

    Visual Basic

    Private Shared ReadOnly _categoryIdProperty As PropertyInfo(Of System.String) = RegisterProperty(Of System.String)(Function(p As Category) p.CategoryId, String.Empty, RelationshipTypes.PrivateField)
    Private _categoryId As System.String = _categoryIdProperty.DefaultValue
    Public Property CategoryId() As System.String
        Get 
            Return GetProperty(_categoryIdProperty, _categoryId) 
        End Get
        Set (value As System.String)
            SetProperty(_categoryIdProperty, _categoryId, value)
        End Set
    End Property

    This has been fixed in a nightly build of the CodeSmith CSLA Templates and will be in version 3.0.1 of the CodeSmith CSLA Templates. The CSLA team will also be releasing version 4.0.1 of CSLA sometime soon. If you don't have this overload set in CSLA 4.0.1 then you will receive the following exception:

    Properties with private backing fields must be marked as RelationshipTypes.PrivateField

    If you are using the CodeSmith CSLA templates, remember to update to the latest templates and regenerate. If you are not, well you should be using the CodeSmith CSLA Templates as it is about to save you a ton of time adding the RelationshipTypes.PrivateField overload to all of your properties!

  • How to upgrade a Community Server website thats running 2.1, 2007 or 2008 site to Community Server 2008 or Telligent Community 5.5

    I have done a few of these upgrades for a few big sites like Bink.nu and Sober.com. Here is the safest way without a ton of headaches. Please contact me if you need help, I'm willing to do the upgrade for people as well... Please do SQL backups after every step!

    Database Instructions

    1. Upgrade from Community Server 2.1 to 2007.0
    2. Create a SQL backup.
    3. Run SQL Compare against a fresh database of cs2007 to the upgraded database and make sure everything transferred correctly.
    4. If there were changes, create a SQL backup.
    5. Run the upgrade from 2007.0 to the last service pack release of 2007. (I think it was SP3).
    6. Create a SQL backup.
    7. Upgrade from CS 2007 SP3 to CS 2008.
    8. Create a SQL backup.
    9. Run SQL Compare against a freshly created CS 2008 database against the upgrade database to ensure everything is correct.
    10. If there were changes, create a SQL backup.
    11. Upgrade to CS 2008.5 using the upgrade utility.
    12. Create a SQL backup.
    13. Upgrade to latest service pack.
    14. Run SQL Compare against a freshly created CS 2008.5 SP3 database against the upgrade database to ensure everything is correct.
    15. --------------- Continue here if you are going to the latest version of TC 5.5-------
    16. Upgrade to TC 5.5.
    17. Run SQL Compare against a freshly created CS TC 5.5 database against the upgrade database to ensure everything is correct.

    Site File upgrade.

    1. The important thing here is that you need to do the upgrade process in order and make notes of changes. When doing this for a live database, do a run through of everything and make notes of things changing or failing and get them resolved a head of time.
    2. For the upgrade to 2007, read the readme and make sure you copy the storage files into the correct spot.
      1. I would extract the complete 2007 site files and copy the photo storage folders and everything to the correct spot.
    3. To upgrade to 2008, you need to run the upgrade utility.
      1. I would extract the complete 2008 site files and read the read me. I would copy the site files to the correct spots as they were.
      2. Once you are running this tool (somewhat buggy), I would make sure that you check the log file and copy over the file storage in the utilities folder to the file storage folder on the destination site.
      3. Then I would copy the old file storage folders to the same locations in the new site... I've had cases where they are hard linked and then you get missing files. I would only ever delete these folders after completely upgraded and you have them backed up...
    4. To upgrade to 2008.5, I'm pretty sure it's using the upgrade utility again, go back to step 3.
    5. To upgrade to TC 5.5, you need to extract the clean website (to a new directory).
      1. Next, copy over only the file storage folder (leave the old file storage folders \photos\, \blogs\, \forums\ for now (just to be safe).

    Final Steps.

    1. Copy the files from steps 4 or 5 to the web server.
    2. Replace permissions on all upgraded files.
    3. You will need to make sure that all of your custom scripts are updated accordingly.
    4. Make sure all images and links are working.

  • How-to: Preserve column names in generated PLINQO entities.

     

    There may come a time where you need to rename a column in your generated entity for whatever reason. The following tip and trick article will show you how to quickly accomplish this.

    Let's assume you have a generated property for the column AccountId and it is defined like this:

    [System.CodeDom.Compiler.GeneratedCode("CodeSmith", "5.0.0.0")]
    private int _accountId = default(int);
    
    /// <summary>
    /// Gets the AccountId column value.
    /// </summary>
    [System.Data.Linq.Mapping.Column(Name = "AccountId", Storage = "_accountId", DbType = "int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
    [System.Runtime.Serialization.DataMember(Order = 1)]
    [System.CodeDom.Compiler.GeneratedCode("CodeSmith", "5.0.0.0")]
    public int AccountId
    {
        get { return _accountId; }
        set
        {
            if (_accountId != value)
            {
                OnAccountIdChanging(value);
                SendPropertyChanging("AccountId");
                _accountId = value;
                SendPropertyChanged("AccountId");
                OnAccountIdChanged();
            }
        }
    }

    If you take a look at the DBML file, it will look like this:

    <Type Name="Account">
      <Column Name="AccountId" Storage="_accountId" Type="System.Int32" DbType="int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
    </Type>

    Say you want to change the name of the property and have it preserved across generation. You will need to open the generated DBML file with the designer and change the name. I recommend always opening up the DBML file with an Xml Text editor (right click on the dbml file and select open with) and update the definition like this. Please note that opening the DBML file in the class designer will cause custom DBML data to be lost!

     

    <Type Name="Account">
      <Column Name="AccountId" Member="Id" Storage="_id" Type="System.Int32" DbType="int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
    </Type>

    Notice that an Member Attribute was added and the Storage Attribute value was changed. These are the two attributes that you will need to modify in order to rename properties in the DBML file. If you regenerate and take a look at the generated partial class. You will see that the AccountID property was dropped and the Id Property was created:

     

    [System.CodeDom.Compiler.GeneratedCode("CodeSmith", "5.0.0.0")]
    private int _id = default(int);
    
    /// <summary>
    /// Gets the AccountId column value.
    /// </summary>
    [System.Data.Linq.Mapping.Column(Name = "AccountId", Storage = "_id", DbType = "int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
    [System.Runtime.Serialization.DataMember(Order = 12)]
    [System.CodeDom.Compiler.GeneratedCode("CodeSmith", "5.0.0.0")]
    public int Id
    {
        get { return _id; }
        set
        {
            if (_id != value)
            {
                OnIdChanging(value);
                SendPropertyChanging("Id");
                _id = value;
                SendPropertyChanged("Id");
                OnIdChanged();
            }
        }
    }

     

1 2 3 4 5 Next > ... Last »
windowscoding.com;
Copyright © 2008 Windows Coding
Microsoft and Microsoft logo's are trademarks of Microsoft Corporation.