Solved

How to override a method in a class that implements an interface in C#

Posted on 2014-07-30
4
276 Views
Last Modified: 2014-07-30
I have a class that is compiled into its own DLL.  The purpose of the class is to interface with a database that can be an ORACLE or SQL Server database.  I created the class as public partial class DatabaseMethods : IDatabaseMethods.  I'm including the class code here but have removed the methods that aren't needed for this question.
namespace DatabaseMethodsClass
{
    public interface IDatabaseMethods
    {
        string GetDatabaseConnection(int dbid);
        string GetEFConnectionString();
    }

    public partial class DatabaseMethods : IDatabaseMethods
    {
        private string _serversxml;
        public virtual string serversxml
        {
            get { return _serversxml; }
            set { this._serversxml = @"file://PL-WLMSPTPV04/websites/i21/Includes/xml/Servers.xml"; }
        }

        private HttpContextBase _httpContext = null;
        private CryptorEngine _CryptorEngine = null;
        public DatabaseMethods() : this(new HttpContextWrapper(HttpContext.Current), new CryptorEngine()) { }
        public DatabaseMethods(HttpContextBase hcb, CryptorEngine ce)
        {
            _httpContext = hcb;
            _CryptorEngine = ce;
        }

// Other methods removed to make this easier to read

        public virtual string GetDatabaseConnection(int dbid)
        {

            // Servers.xml contains a list of all the servers Intranet Support Team Applications are deployed to;
            // Also contains the Connection Strings for the Application Catalog application for each environment (DEV, UAT, PROD)
            XmlDocument xmldoc = LoadServersXML(serversxml);

            string host = DetermineHost();
            int env = DetermineEnvironmentFromHost(host, xmldoc);
            string appcatalogcs = GetAppCatalogConnectionString(env);

            // Get Database record
            AppCatalog_DBConnection db = new AppCatalog_DBConnection();
            db = GetDBConnectionRecord(dbid, appcatalogcs, env);
            return FormatConnectionString(db);
        }

        public virtual string GetEFConnectionString()
        {
            // Servers.xml contains a list of all the servers Intranet Support Team Applications are deployed to;
            // Also contains the Connection Strings for the Application Catalog application for each environment (DEV, UAT, PROD)
            XmlDocument xmldoc = LoadServersXML(serversxml);

            string host = DetermineHost();
            int envid = DetermineEnvironmentFromHost(host, xmldoc);

            // Create EF Connection String based on environment
            string EFConnectionString = "";
            switch (envid)
            {
                case 1:     // DEV
                    EFConnectionString = @"metadata=res://*/Models.AppCatalog.csdl|res://*/Models.AppCatalog.ssdl|res://*/Models.AppCatalog.msl;provider=System.Data.SqlClient;provider connection string='data source=servername;initial catalog=Catalog;persist security info=True;user id=username;password=password;multipleactiveresultsets=True;App=EntityFramework'";
                    break;
                case 2:     // UAT
                    EFConnectionString = @"metadata=res://*/Models.AppCatalog.csdl|res://*/Models.AppCatalog.ssdl|res://*/Models.AppCatalog.msl;provider=System.Data.SqlClient;provider connection string='data source=servername;initial catalog=Catalog;persist security info=True;user id=username;password=password;multipleactiveresultsets=True;App=EntityFramework'";
                    break;
                case 3:     // PROD
                    EFConnectionString = @"metadata=res://*/Models.AppCatalog.csdl|res://*/Models.AppCatalog.ssdl|res://*/Models.AppCatalog.msl;provider=System.Data.SqlClient;provider connection string='data source=servername;initial catalog=Catalog;persist security info=True;user id=username;password=password;multipleactiveresultsets=True;App=EntityFramework'";
                    break;
                default:
                    // Throw error - AppCatalog environment doesn't exist in Servers.xml
                    throw new ValidationException(String.Format("AppCatalog Connection String does not exist for Environment {0} in Server Configuration File", envid));
            }
            return EFConnectionString;
        }
    }

    public partial class AppCatalog_DBConnection
    {
        public int dbconnectionID { get; set; }
        public string dbconnectionUserName { get; set; }
        public string dbconnectionUnsecuredPassword { get; set; }
        public string dbconnectionServer { get; set; }
        public string dbconnectionPortNumber { get; set; }
        public string dbconnectionServiceName { get; set; }
        public string dbconnectionCatalog { get; set; }
        public string dbconnectionSID { get; set; }
        public int dbconnectionEnvironmentID { get; set; }
        public int dbconnectionDatabaseID { get; set; }
        public int dbconnectionServerTypeID { get; set; }
    }
}

Open in new window


Now I have an app that wants to use the DatabaseMethods class but needs to provide its own version of the GetEFConnectionString Method.

How would I do that?  If I create a new class that implements the Interface and implement the 2 methods in the interface, how do I get access to all of the private methods in the DatabaseMethods class?  I need the class to be an interface so I can do Dependency Injection for my testing.

Here is what I have so far but don't konw how to tell it to implement the Interface also.

public class REF_DatabaseMethods : DatabaseMethods
{
     public override string GetEFConnectionString()
     {
            // my override code
     }
}

Open in new window


Any help is greatly appreciated!
0
Comment
Question by:dyarosh
  • 2
  • 2
4 Comments
 
LVL 32

Expert Comment

by:ste5an
ID: 40229398
This concept is called Dependency Inversion Principle (DSP - the D in SOLID). One consequence of it is: the interface should be declared in the orbit of the consumer assembly (higher level), not in the assembly containing the concrete implementation (lower level).

// Placed in the higher-level assembly.
namespace DatabaseConsumer
{
	public interface IDatabaseMethods
	{
		string GetDatabaseConnection(int dbid);
		string GetEFConnectionString();
	}
}

// Placed in one of the lower-level assemblies.
// Which is referenced in the DI container (Unity, MEF).
namespace DatabaseProviderDevelopment
{
    public class DatabaseMethods : IDatabaseMethods
    {
        public string GetDatabaseConnection(int dbid) { }
		string GetEFConnectionString() {};
    }
}

// Placed in one of the lower-level assemblies.
// Which is referenced in the DI container (Unity, MEF).
namespace DatabaseProviderProduction
{
    public class DatabaseMethods : IDatabaseMethods
    {
        public string GetDatabaseConnection(int dbid) { }
		string GetEFConnectionString() {};
    }
}

Open in new window


Now for the code smells:
1. An interface named database methods makes no sense. It's either a catch basin, then the methods belong to different interfaces. Or it is badly named, it does not transport the usage in its name.
2. Methods should never use the verbs Get and Set. Except they are property getter/setter methods. As we have properties in C#, use them instead in your interface declaration.
3. A connection (GetDatabaseConnection) is not a string. string as a return type makes no sense.
4. A better name: CreateDatabaseConnection.
5. You don't need virtual methods, as long as you don't want to override some of your methods.
6.
        public virtual string GetDatabaseConnection(int dbid)
        {

            // Servers.xml contains a list of all the servers Intranet Support Team Applications are deployed to;
            // Also contains the Connection Strings for the Application Catalog application for each environment (DEV, UAT, PROD)
            XmlDocument xmldoc = LoadServersXML(serversxml);
            return FormatConnectionString(db);
        }

Open in new window

You're describing entities. Implement them. Use XML serialziation.
7. Sounds like your server list should be also decoupled and injected.
8. Never hardcode connection strings. Use .config files instead of it.
9. switch() indicates often (really often) that you can and should use inheritance. See my sample above.
10.Your class AppCatalog_DBConnection does not represent a connection. It's a data transfer object (DTO, POCO), which holds the configuration information for a connection. Name it accordingly.
11. I would guess from your code, the setters of AppCatalog_DBConnection should be private.
0
 

Author Comment

by:dyarosh
ID: 40229778
I appreciate your comments but need some clarification.

1. The DatabaseMethods Class (I will come up with a better name) purpose is to "create" (as you suggested) the Connection String for a database that would be used as a parameter into an OracleConnection object or SqlConnection object which takes a string as a parameter.  That is why the the method returns a string.  The information needed to create the connection string is stored in a database.  The dbid parameter in the method call is used to determine which database record to retrieve for the information.

We have many applications and environments that those applications run in (i.e. DEV, UAT, PROD) which is why we don't store the connection strings in config files.  We also don't want the sensitive information such as username and password stored in flat file config files.

With that said, based on your recommendations I would remove the Interface definition from the same file and assembly as the definition for the DatabaseMethods class and put it in the same assembly as the class that is consuming the DatabaseMethods object.  Is that correct?

VS Studio Project 1 (DatabaseProviderDevelopment.DLL)
namespace DatabaseProviderDevelopment
{
    public class DatabaseMethods : IDatabaseMethods
    {
        public string GetDatabaseConnection(int dbid) { }
		string GetEFConnectionString() {};
    }
}

VS Studio Project 2 (Project.DLL)
namespace DatabaseConsumer
{
	public interface IDatabaseMethods
	{
		string GetDatabaseConnection(int dbid);
		string GetEFConnectionString();
	}
}

Open in new window


If the above is correct and the interface is declared in a separate assembly, how do I get the first assembly to compile since IDatabaseMethods isn't defined in the assembly?  

If the Interface is defined in the Higher Assembly then every project that wants to use the DatabaseMethods object would have to know to define the interface which doesn't make sense to me.

I want to have one class that will have a method for creating the connection string and also a method to create the EF connection string.  For the most part, creating the connection string won't need to be overridden unless we add a new database to our mix.  However creating the EF Connection String will need to be overridden by each project that consumes the object that uses the EF.

So how do I structure the DatabaseMethods class so that it can be consumed by many applications?  I apologize for my ignorance but we are in the process of converting all of our legacy classic asp applications to asp.net and I want to get the infrastructure setup using SOLID OOP which is new to our group.
0
 
LVL 32

Accepted Solution

by:
ste5an earned 500 total points
ID: 40229923
OracleConnection and SqlConnection are derived from DbConnection. Thus I would abstract it on this level. Then you don't need to handle connection strings.

The dbid parameter in the method call is used to determine which database record to retrieve for the information.
What kind of database is involved here?

We also don't want the sensitive information such as username and password stored in flat file config files.
It makes basically no difference, whether you store the information in a .config file or database.

With that said, based on your recommendations I would remove the Interface definition from the same file and assembly as the definition for the DatabaseMethods class and put it in the same assembly as the class that is consuming the DatabaseMethods object.  Is that correct?
Yes, that's correct.

If the above is correct and the interface is declared in a separate assembly, how do I get the first assembly to compile since IDatabaseMethods isn't defined in the assembly?
You reference it in your project.

If the Interface is defined in the Higher Assembly then every project that wants to use the DatabaseMethods object would have to know to define the interface which doesn't make sense to me.
It's quite simple: It's to avoid breaking the higher level consumer. When the interface is part of the lower level component, then this component is responsible for the interface. This means, that the interface would be independent of higher level components, thus you may change it whenever you want.
This would break the higher level code. It could not be longer compiled against the interface.
Thus it's called dependency inversion. The roles are turned up-side down.
You may see the interface when belonging to the higher level component as a contract: I can use an contractor/provider which fulfills this contract. See also SOLID. Another simple reason: When the interface is in the lower level, then we don't need it. We can simply link against the classes.

However creating the EF Connection String will need to be overridden by each project that consumes the object that uses the EF.
Overriding methods is only necessary when you want to inherit from classes, not from interfaces.

So how do I structure the DatabaseMethods class so that it can be consumed by many applications?
You need always to look onto your layer from above. The DatabaseMethods sound like they are meant to be your data access layer (DAL). Thus its normal purpose is to abstract that there may be different database kinds (Oracle, SqlServer). When you pass connection strings, then the layer above still needs to know, what database is used. Otherwise it could not create the correct connection object.

btw, here is also the point, where you should take your time and make yourself familiar with especially the factory method pattern of the creational patterns. Another good source: sourcemaking.com.
0
 

Author Closing Comment

by:dyarosh
ID: 40230673
Thank you for your insight and the links.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Article by: Ivo
C# And Nullable Types Since 2.0 C# has Nullable(T) Generic Structure. The idea behind is to allow value type objects to have null values just like reference types have. This concerns scenarios where not all data sources have values (like a databa…
Introduction Hi all and welcome to my first article on Experts Exchange. A while ago, someone asked me if i could do some tutorials on object oriented programming. I decided to do them on C#. Now you may ask me, why's that? Well, one of the re…
This video discusses moving either the default database or any database to a new volume.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now