Nik's Technology Blog

Travels through programming, networks, and computers

No defining declaration found for implementing OnValidate(System.Data.Linq.ChangeAction)

If you happen to be getting an error message like the one below, then read on.

Error    1    No defining declaration found for implementing declaration of partial method 'mvcCMS.Models.WebPage.OnValidate(System.Data.Linq.ChangeAction)'    C:\mvcCMS\Models\WebPage.cs    28    22    mvcCMS


I'm using LINQ to SQL designer in Visual Studio to create a database schema and I'm using a partial class to extend the code generated by the designer.

In the example below I am using the pattern used by NerdDinner.com to add business rules/validation to the model classes LINQ to SQL built based on my database schema.

namespace mvcCMS.Models
{
    public partial class WebPage
    {
        public bool IsValid
        {
            get { return (GetRuleViolations().Count() == 0); }
        }
        public IEnumerable<RuleViolation> GetRuleViolations()
        {
            if (String.IsNullOrEmpty(Title))
                yield return new RuleViolation("Title is required", "Title");
            if (String.IsNullOrEmpty(Text))
                yield return new RuleViolation("Web copy is required", "Text");

            yield break;
        }
        partial void OnValidate(ChangeAction action)
        {
            if (!IsValid)
                throw new ApplicationException("Rule violations prevent saving");
        }
    }
}

Where OnValidate() is a partial method LINQ to SQL provides which enables us to be notified when the object is about to be persisted to the database, so we can check all our business rules have been met before the object is flushed to the database.

An empty OnValidate() method is part of the designer generated code for your data class located in the #region Extensibility Method Definitions and it seems that these Extensibility Method Definitions only get added to the designer code when your tables have primary keys.

When a table is dragged onto the Object Relational Designer in Visual Studio the classes that are generated will only implement INotifyPropertyChanging and INotifyPropertyChanged if your tables have primary keys.  If the classes don't implement these interfaces the code won't implement the OnValidate() method, and if the OnValidate() method doesn't exist your partial class won't compile.

The Solution

The solution is simple.  Add a primary key to your database table, delete the associated data class from the Object Relational Designer and then drag the database table from Server Explorer back onto the Object Relational Designer surface.

You should then find the designer generated code now implements INotifyPropertyChanging and INotifyPropertyChanged and the class contains a definition for OnValidate() in the #region Extensibility Method Definitions.  Your code should now compile.

TargetInvocationException - Exception has been thrown by the target of an invocation

This exception isn't very useful because it's a general exception thrown when a method invoked through reflection throws an exception, took me a while to figure out what the issue was.  Even though I knew the page causing the error.

This exception was thrown by a ASP.NET web form which contained a GridView connected to an ObjectDataSource.

The ObjectDataSource references methods in a data access layer class.  These methods then call stored procedures in the MS SQL database. 

The code worked perfectly in my development environment.

I have the SQL database set up so that the database user ASP.NET uses only has rights to execute the stored procedures it needs to.  The database user cannot run commands against the tables directly, this way I limit the surface area of attack should my application have a weakness that could be exploited.

I double checked the stored procedures were all up-to-date, then double checked all the permissions on the stored procedures, and everything seemed in order, but I still kept getting the stack trace below in Event Viewer:


    Stack trace:    at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Web.UI.WebControls.ObjectDataSourceView.InvokeMethod(ObjectDataSourceMethod method, Boolean disposeInstance, Object& instance)
   at System.Web.UI.WebControls.ObjectDataSourceView.ExecuteSelect(DataSourceSelectArguments arguments)
   at System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback)
   at System.Web.UI.WebControls.DataBoundControl.PerformSelect()
   at System.Web.UI.WebControls.BaseDataBoundControl.DataBind()
   at System.Web.UI.WebControls.GridView.DataBind()
   at System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound()
   at System.Web.UI.WebControls.CompositeDataBoundControl.CreateChildControls()
   at System.Web.UI.Control.EnsureChildControls()
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Custom event details:

I then decided to check the stored procedures.  Then it dawned on me that one of the stored procedures used EXEC sp_executesql, which requires the database user to have, in my case, SELECT permissions on the actual table itself.

Once I granted these permissions to the database user, the web form loaded correctly.

Enumerate Available Database Providers in ASP.NET

Using the DbProviderFactories class in ADO.NET you can retrieve a list of available database factories using the GetFactoryClasses method. This can be useful if you host your ASP.NET site on a shared server and don't have access to the web server and machine.config file.

The output of the GetFactoryClasses method is a DataTable of available factory classes, these are the factory classes that the .NET runtime will have access to.

Here's the kind of output you'll get...

NameDescriptionInvariantNameAssemblyQualifiedName
Odbc Data Provider .Net Framework Data Provider for Odbc System.Data.Odbc System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
OleDb Data Provider .Net Framework Data Provider for OleDb System.Data.OleDb System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
OracleClient Data Provider .Net Framework Data Provider for Oracle System.Data.OracleClient System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
SqlClient Data Provider .Net Framework Data Provider for SqlServer System.Data.SqlClient System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Here's the code you'll need in your code behind file.

DataTable providersList = null;
providersList = System.Data.Common.DbProviderFactories.GetFactoryClasses();
GridView providerGridView = new GridView();
providerGridView.DataSource = providersList;
providerGridView.DataBind();
form1.Controls.Add(providerGridView);