0
votes

I am building a RESTful WCF service that uses EF 6 to access POCOs. I also use Ninject and the repository pattern to access the database. Here's my service setup:

Configuration

 <serviceBehaviors>
    <behavior name="commonServicebehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="sbLoginSAS">
      <transportClientEndpointBehavior>
        <tokenProvider>
          <sharedAccessSignature keyName="RootManageSharedAccessKey" key="oJh2klf242iwISUDcsTQHeR/3W3FlQTQte/M=" />
        </tokenProvider>
      </transportClientEndpointBehavior>
      <webHttp defaultOutgoingResponseFormat="Json" />
      <serviceRegistrySettings discoveryMode="Public" />
    </behavior>

Contract

[ServiceContract]
public interface ILoginService
{
    [OperationContract]
    [WebInvoke(UriTemplate = "")]
    ErrorCollectionDTO Login(LoginRequestDTO loginRequest);
}

Implementation

class LoginService : ILoginService
{
    private readonly ILoginValidator _loginValidator;
    private readonly IErrorMapper _errorMapper;
    private readonly IEncryptor _encryptor;
    private readonly IUserManager _userManager;

    public LoginService(ILoginValidator loginValidator, IErrorMapper errorMapper, IEncryptor encryptor, IUserManager userManager)
    {
        _loginValidator = loginValidator;
        _errorMapper = errorMapper;
        _encryptor = encryptor;
        _userManager = userManager;
    }

    public ErrorCollectionDTO Login(LoginRequestDTO loginRequest)
    {
        //Validate the request
        var completeSummary = _loginValidator.Validate(loginRequest);

        //Maps validation errors to a response DTO
        var errorCollectionDTO = _errorMapper.Transform(completeSummary);

        //If request not valid, return errors
        if (!errorCollectionDTO.IsValid) return errorCollectionDTO;

        //At this point, user is correctly authenticated
        //so we prepare and send him an authorization token
        var user = _userManager.GetUser(loginRequest.CPR);
        var token = _encryptor.Encrypt(loginRequest.CPR);

        user.SecurityToken = new SecurityToken
        {
            Token = token,
            ValidUntil = DateTime.Now.AddMinutes(5)
        };

        _userManager.UpdateUser(user);

        return new LoginResponseDTO(errorCollectionDTO, token.ToString());
    }

I use DTOs for all requests and responses that are all using the [DataContract] / [DataMember] attributes. The service accepts a JSON request properly. However I get an error in the line:

var completeSummary = _loginValidator.Validate(loginRequest);

when the calling method tries to retrieve the entries of an entity through its repository with _userManager.GetUser(entity.CPR):

 private IEnumerable<IError> ValidateEntity(LoginRequestDTO entity)
    {
        if (entity == null)
            yield return new Error("Login request was null or invalid");
        else
        {
            var user = _userManager.GetUser(entity.CPR);

            if (user == null)
                yield return new Error("User doesn't exist or password is wrong");
        }
    }


Type 'System.Data.Entity.DbSet`1[ITU.MAP.Domain.Model.User]' cannot be serialized.
Consider marking it with the DataContractAttribute attribute, and marking all of its
members you want serialized with the DataMemberAttribute attribute.  If the type is
a collection, consider marking it with the CollectionDataContractAttribute.

As far as I understand, WCF tries to serialise the db entries the method calls. I thought that shouldn't be a problem since I'm using DTOs for responses and that's nowhere near the response construction. I googled around and I tried some solutions such as

Configuration.ProxyCreationEnabled = false;

and

Configuration.LazyLoadingEnabled = false;

but they didn't work. Of course, I also added the DataContract and DataMember attributes to all my POCOs as the error message indicates but to no avail.

Update: Here's the POCO that WCF tries to serialise. I also tried with other, non-abstract classes decorated with just [DataContract] and [DataMember] but the same error occurs. Child types in KnownType are also decorated properly with the aforementioned attributes.

[DataContract(IsReference = true)]
[KnownType(typeof(Paramedic))]
[KnownType(typeof(Resident))]
[Table("Users")]
public abstract class User : IIdentifiable
{
    [Key]
    [DataMember]
    [DisplayName("Id")]
    public int Id { get; set; }

    [DataMember]
    [DisplayName("CPR")]
    public string CPR { get; set; }

    [DataMember]
    [DisplayName("Password hash")]
    public byte[] Password { get; set; }

    [DataMember]
    [DisplayName("Name")]
    public string Name { get; set; }

    [DataMember]
    [DisplayName("Surname")]
    public string Surname { get; set; }

    [DataMember]
    [DisplayName("Region")]
    public int RegionId { get; set; }

    [DataMember]
    [DisplayName("Region")]
    [ForeignKey("RegionId")]
    public virtual Region Region { get; set; }

    [DataMember]
    [DisplayName("Age")]
    public int Age { get; set; }

    [DataMember]
    [DisplayName("Blood type")]
    public BloodType BloodType { get; set; }

    [DataMember]
    [DisplayName("Rhesus")]
    public Rhesus Rhesus { get; set; }

    [DataMember]
    public int? SecurityTokenId { get; set; }

    [DataMember]
    [DisplayName("Security token")]
    [ForeignKey("SecurityTokenId")]
    public virtual SecurityToken SecurityToken { get; set; }

    [DataMember]
    public virtual ICollection<Allergy> Allergies { get; set; }

    [DataMember]
    public virtual ICollection<Condition> Conditions { get; set; }

    [DataMember]
    public virtual ICollection<Prescription> Prescriptions { get; set; }
}
2

2 Answers

0
votes

Try to decorate the class with data contract and the properties with data member attributes.

0
votes

It seems that WCF can't serialise the IQueryable type that I used in my generic repository class methods to return collections of database results. It worked by using List instead, which is very icky if you ask me. If anyone has a better suggestion than using List, please do mention it.