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; }
}