سؤال

I have a solution structure where the contracts (Data/Service etc) are in a different project from the business entities and I am using Automapper to map between from a third Service implementation project.

WCFProject.Service.BusinessLayer
WCFProject.Service.Contracts
WCFProject.Service.Impl

My ServiceImpl has reference to both these other projects and the automapping from DataContract to BusinessEntity is done here and then the correct method is called on the BusinessEntity object

Now, I want to add a few FaultContracts and then use them in my business logic to throw the right exceptions. But if I add them to the Contracts project (which is ideal since I want to keep all my contracts together), then I need a reference from BusinessLayer to Contracts to use them in BusinessLayer. I wanted to keep these independent if possible and just deal with DTOs between these two layers. Is that a valid agrument from my side that I want to keep the two projects independent? Do you map exceptions as well? Or is there a better way to deal with this.

هل كانت مفيدة؟

المحلول 2

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.

نصائح أخرى

Your business layer should have not knowlage of the above layers. So it does not know anything about that you have a wcf layer on top. Throwing fault is something from your wcf layer, catch your exception there and dicide what you want to do. A business exception can be mapped to a wcf fault, but if you have a connection of nullpointer exception you just want to give a general fault that something is wrong.

A sample of handing/mapping errors in a service behavoir can be found here: WCF-Exception-Handling

If you are going through the trouble of creating a DTO layer, then you probably want to protect the outside world from the inner workings of your domain.

In the same vein, you should protect the outside world from every exception that is possible inside your domain.

IMHO, you should do the work of catching your domain exceptions, deciding what you want (or do not want) exposed, and them map that to the proper error in a DTO exception format. For example, you might not want to expose stack traces outside your domain.

As a general rule, I try to only expose exceptions that the client can do something about. If the database is down, then what is the client going to do about it? So, maybe the client doesn't need that SqlException but instead needs a 500 Internal Server Error.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top