views:

517

answers:

3

Let me preface this by saying I now realize how stupid I am and was. I have been developing for 1 year (to the day) and this was the first thing I wrote. I now have come back to it and I can't make heads or tails of it. It worked at one point on a very simple app but that was a while ago.

Specifically I am having problems with LocalDBConn which uses out but for the life of me I can't remember why.

Guidance, pointer's, refactoring, slaps up side the head are ALL welcome and appreciated!

public class MergeRepl
{
            // Declare nessesary variables
    private string subscriberName;
    private string publisherName;
    private string publicationName;
    private string subscriptionDbName;
    private string publicationDbName;
    private MergePullSubscription mergeSubscription;
    private MergePublication mergePublication;
    private ServerConnection subscriberConn;
    private ServerConnection publisherConn;
    private Server theLocalSQLServer;
    private ReplicationDatabase localRepDB;

    public MergeRepl(string subscriber, string publisher, string publication, string subscriptionDB, string publicationDB)
    {
        subscriberName = subscriber;
        publisherName = publisher;
        publicationName = publication;
        subscriptionDbName = subscriptionDB;
        publicationDbName = publicationDB;

        //Create connections to the Publisher and Subscriber.
        subscriberConn = new ServerConnection(subscriberName);
        publisherConn = new ServerConnection(publisherName);


        // Define the pull mergeSubscription
        mergeSubscription = new MergePullSubscription
                                {
                                    ConnectionContext = subscriberConn,
                                    DatabaseName = subscriptionDbName,
                                    PublisherName = publisherName,
                                    PublicationDBName = publicationDbName,
                                    PublicationName = publicationName
                                };

        // Ensure that the publication exists and that it supports pull subscriptions.
        mergePublication = new MergePublication
                               {
                                   Name = publicationName,
                                   DatabaseName = publicationDbName,
                                   ConnectionContext = publisherConn
                               };

        // Create the local SQL Server instance
        theLocalSQLServer = new Server(subscriberConn);
        // Create a Replication DB Object to initiate Replication settings on local DB
        localRepDB = new ReplicationDatabase(subscriptionDbName, subscriberConn);

        // Check that the database exists locally
        CreateDatabase(subscriptionDbName);
    }

    public void RunDataSync()
    {
        // Keep program from appearing 'Not Responding'
        ///// Application.DoEvents();

        // Does the needed Databases exist on local SQLExpress Install
        /////CreateDatabase("ContactDB");

        try
        {
                 //  Connect to the Subscriber
                 subscriberConn.Connect();

            // if the Subscription exists, then start the sync
                 if (mergeSubscription.LoadProperties())
                 {
                     // Check that we have enough metadata to start the agent
                     if (mergeSubscription.PublisherSecurity != null || mergeSubscription.DistributorSecurity != null)
                     {
                         //  Synchronously start the merge Agent for the mergeSubscription
                         //  lblStatus.Text = "Data Sync Started - Please Be Patient!";
                         mergeSubscription.SynchronizationAgent.Synchronize();
                     }
                     else
                     {
                         throw new ApplicationException("There is insufficient metadata to synchronize the subscription." +
                             "Recreate the subscription with the agent job or supply the required agent properties at run time.");
                     }
                 }
                 else
                 {
                     // do something here if the pull mergeSubscription does not exist
                     // throw new ApplicationException(String.Format("A mergeSubscription to '{0}' does not exist on {1}", publicationName, subscriberName));
                     CreateMergeSubscription();
                 }
        }
        catch (Exception ex)
        {
            // Implement appropriaate error handling here
            throw new ApplicationException("The subscription could not be synchronized.  Verify that the subscription has been defined correctly.", ex);
            //CreateMergeSubscription();
        }
        finally
        {
            subscriberConn.Disconnect();
        }
    }

    public void CreateMergeSubscription()
    {
        // Keep program from appearing 'Not Responding'
        // Application.DoEvents();

        try
        {

            if (mergePublication.LoadProperties())
            {
                if ((mergePublication.Attributes & PublicationAttributes.AllowPull) == 0)
                {
                    mergePublication.Attributes |= PublicationAttributes.AllowPull;
                }

                // Make sure that the agent job for the mergeSubscription is created.
                mergeSubscription.CreateSyncAgentByDefault = true;

                // Create the pull mergeSubscription at the Subscriber.
                mergeSubscription.Create();

                Boolean registered = false;

                // Verify that the mergeSubscription is not already registered.
                foreach (MergeSubscription existing in mergePublication.EnumSubscriptions())
                {
                    if (existing.SubscriberName == subscriberName
                        && existing.SubscriptionDBName == subscriptionDbName
                        && existing.SubscriptionType == SubscriptionOption.Pull)
                    {
                        registered = true;
                    }
                }
                if (!registered)
                {
                    // Register the local mergeSubscription with the Publisher.
                    mergePublication.MakePullSubscriptionWellKnown(
                        subscriberName, subscriptionDbName,
                        SubscriptionSyncType.Automatic,
                        MergeSubscriberType.Local, 0);
                }
            }
            else
            {
                // Do something here if the publication does not exist.
                throw new ApplicationException(String.Format(
                    "The publication '{0}' does not exist on {1}.",
                    publicationName, publisherName));
            }
        }
        catch (Exception ex)
        {
            // Implement the appropriate error handling here.
            throw new ApplicationException(String.Format("The subscription to {0} could not be created.", publicationName), ex);

        }
        finally
        {
            publisherConn.Disconnect();
        }
    }

    /// <summary>
    /// This will make sure the needed DataBase exists locally before allowing any interaction with it.
    /// </summary>
    /// <param name="whichDataBase">The name of the DataBase to check for.</param>
    /// <returns>True if the specified DataBase exists, False if it doesn't.</returns>
     public void CreateDatabase(string whichDataBase)
    {
        Database db;
        LocalDBConn(whichDataBase, out theLocalSQLServer, out localRepDB, out db);


        if (!theLocalSQLServer.Databases.Contains(whichDataBase))
        {
            //Application.DoEvents();
            // Create the database on the instance of SQL Server.
            db = new Database(theLocalSQLServer, whichDataBase);
            db.Create();

        }

        localRepDB.Load();
        localRepDB.EnabledMergePublishing = false;
        localRepDB.CommitPropertyChanges();

        if (!mergeSubscription.LoadProperties())
        {
            CreateMergeSubscription();
        }

    }

    private void LocalDBConn(string databaseName, out Server server, out ReplicationDatabase replicationDatabase, out Database db)
    {
        db = server.Databases[replicationDatabase.Name];
    }

    /// <summary>
    /// Checks for the existince of the Publication.  If there is one it verify's Allow Pull is set
    /// </summary>
    /// <returns>True if Publication is present. False if not.</returns>
    public bool CheckForPublication()
    {
        // If LoadProperties() returns TRUE then the Publication exists and is reachable
        if (mergePublication.LoadProperties())
            return true;

        if ((mergePublication.Attributes & PublicationAttributes.AllowPull) == 0)
        {
            mergePublication.Attributes |= PublicationAttributes.AllowPull;
        }

        return false;
    } // end CheckForPublication()

    /// <summary>
    /// Checks for the existence of a Subscription.
    /// </summary>
    /// <returns>True if a Subscription is present.  False if not</returns>
    public bool CheckForSubscription()
    {
        // Check for the existence of the Subscription
        return mergeSubscription.IsExistingObject;
    } // end CheckForSubscription()
}

Edit 1

Opps, I forgot the specific errors. On server and replicationDatabase I am getting a "Out parameter might not be initialized before accessing"

        private void LocalDBConn(string databaseName, out Server server, out ReplicationDatabase replicationDatabase, out Database db)
    {
        db = server.Databases[replicationDatabase.Name];
    }
+1  A: 

All the compiler is telling you is that the Server and ReplicationDatabase are output parameters and yet you are not assigning anything to them before returning from the LocalDBConn method.

When using out parameters you should perform a null check on them to ensure they have been initialized outside the method, if not initialize them.

Peter
+1  A: 
private void LocalDBConn(string databaseName, out Server server, out ReplicationDatabase replicationDatabase, out Database db)
{
    db = server.Databases[replicationDatabase.Name];
}

This method will not build because the out parameters are not initialized before the method returns. An out parameter allows the caller to pass an uninstantiated object into the method, which the method must then initialize through all paths before it returns (excluding if the method throws an exception). Basically, an out parameter is a statement by the method saying "I will instantiate this object before returning". In LocalDBConn, you are not doing this.

In the C# 3.0 language specification this detailed in section 5.1.6.

Timothy Carter
+1  A: 

Looks like you'd be safe to remove the out's.

I'm not sure how that even compiled, unless maybe it was with VS2003 and the compiler didn't check for this type of error.

From MSDN: Although variables passed as out arguments do not have to be initialized before being passed, the called method is required to assign a value before the method returns.

private void LocalDBConn(string databaseName, Server server, 
      ReplicationDatabase replicationDatabase, out Database db)
{
    db = server.Databases[replicationDatabase.Name];
}

or

private Database LocalDBConn(string databaseName, Server server, 
     ReplicationDatabase replicationDatabase)
{
    return server.Databases[replicationDatabase.Name];
}

Then update your code in CreateDatabase to:

Database db;
LocalDBConn(whichDataBase, theLocalSQLServer, localRepDB, out db);

or

Database db = LocalDBConn(whichDataBase, theLocalSQLServer, localRepDB);
Chris Persichetti
Resharper tells me to make LocalDBConn Static....should I?Thanks for the help!
Refracted Paladin
Yes. No harm in that. Or maybe just get rid of the method and use Database db = theLocalSQLServer.Databases[localRepDB.Name]; Really just a coding preference. Using the method is nice for self documenation though I must say.
Chris Persichetti