Custom Exceptions based off the Visual Studio snippet I'm creating my own application exception types so I could catch them higher in the call stack and take specific action. I'm looking to get feedback on the latest exception I've written for two reasons:


I'm using the Exception snippet from Visual Studio 2013, and am wondering if it is any good and/or has potential problems;
The main parameter for this Exception type would be a string, and I'm wondering what to do with the parameters. Currently I'm following the built in ArgumentException constructor-signatures-wise, but I fear future users of my exception will fall into the same trap as me with ArgumentException: providing a single argument with the paramName, instead of a message.


PS. I've checked the source for ArgumentException and explicitly decided not to put in the extra work of saving the tenantName in a field + property analogously, because the calling code does not yet need it. (Though I'm happy to get feedback on that choice as well of course.)

Bottom line: anything to improve on this custom exception?

[Serializable]
public class TenantNotFoundException : Exception
{
    public TenantNotFoundException() 
    { }

    public TenantNotFoundException(string message)
        : base(message)
    { }

    public TenantNotFoundException(string message, string tenantName)
        : base(string.Format("Tenant '{0}' not found. {1}", tenantName, message))
    { }

    public TenantNotFoundException(string message, Exception inner)
        : base(message, inner)
    { }

    public TenantNotFoundException(string message, string tenantName, Exception inner) 
        : base(string.Format("Tenant '{0}' not found.", tenantName), inner)
    { }

    protected TenantNotFoundException(SerializationInfo info, StreamingContext context)
        : base(info, context) 
    { }
}


PS. I was almost tempted to write just a single constructor with a string parameter for tenant name, but then I wondered (a) if people would be confused and enter a message instead of a tenant name, and (b) why the snippet suggests all the other constructors. This is what prompted me to post this question.



For reference, this is what you start with as the Visual Studio snippet:

[Serializable]
public class MyException : Exception
{
    public MyException() { }
    public MyException(string message) : base(message) { }
    public MyException(string message, Exception inner) : base(message, inner) { }
    protected MyException(
      SerializationInfo info,
      StreamingContext context)
        : base(info, context) { }
}
Using strongly typed data avoids this situation.


One easy and simple way to fix this argument confusion would be passing the user defined datatype instead of string which is reserved for message.

In TenantException sample, if you have Object model for Tenant, pass that object and let the constructor does the remaining with that.

[Serializable]
public class TenantNotFoundException : Exception
{
    public TenantNotFoundException() 
    { }

    public TenantNotFoundException(string message)
        : base(message)
    { }

    public TenantNotFoundException(string message, Tenant tenant)
        : base(string.Format("Tenant '{0}' not found. {1}", tenant.Name, message))
    { }
}