In the question you specify:
I want to add a few FaultContracts and then use them in my business logic to throw the right exceptions.
As you correctly have identified - this introduces a coupling between the public API of your service (service, data and fault contracts) and your business logic. Ideally your business logic should be agnostic to the fact that it is being called by a service, and so a reference to the contracts assembly is troubling.
The contracts assembly should lay out the publicly available information clients have about your service:
namespace Contracts
{
[ServiceContract]
interface IMyService
{
[OperationContract]
[FaultContract(typeof(MyFaultContract))]
[FaultContract(typeof(AnotherFaultContract))]
void MyOperation();
}
[DataContract]
public class MyFaultContract
{
[DataMember]
public string Problem { get; set; }
}
[DataContract]
public class AnotherFaultContract
{
[DataMember]
public string Description { get; set; }
}
}
As with many problems in software development, your issue can be addressed with a layer of indirection. Despite what you specify in the question - you do not want to couple the business logic to the contract assembly. The advantages to not doing so are obvious - it allows the public contracts and the "internal" business logic to evolve independently.
Below shows an example where the service implementation is used to couple the contracts to the business logic. Exceptions in the business layer are mapped to Fault Contracts which are returned to the client:
namespace Service
{
class MyService: IMyService
{
public void MyOperation()
{
try
{
var businessLogic = new BusinessLogic();
businessLogic.DoOperation();
}
catch (KeyNotFoundException)
{
throw new FaultException<MyFaultContract>(new MyFaultContract
{
Problem = "A key issue occurred in the service"
});
}
catch (Exception)
{
throw new FaultException<AnotherFaultContract>(new AnotherFaultContract
{
Description = "Something BAD happened in the service"
});
}
}
}
}
As an aside, it is worth thinking carefully about the Fault Contracts you expose in your client and what information you is required by the client when things go wrong server side. Exposing too much information about exceptions on your service can be a security risk.