Search code examples
c#entity-frameworkwcf-data-servicesodatadto

How can i expose dto objects using wcf data service with ef code first?


I am trying to make a wcf data service where i dont want to get acces to the database models but instead i want to use Data transfer objects. I have been reading a lot on the internet about how to accomplish this but i cant get a good answer for my problem. It is the first time for me doing something with wcf data services so i am a little inexperienced.

Oke here are my models that are linked to my database using Entity Framework

public class User
    {
        [Key]
        public int UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }

        public string CountryCode { get; set; }
        public string PhoneNumber { get; set; }
        public ICollection<User> Contacts { get; set; }

        public virtual Language Language { get; set; }

        public User()
        {
            Contacts = new List<User>();
        }
    }

public class Message
    {
        [Key]
        public int MessageId { get; set; }
        public DateTime SentDate { get; set; }

        public virtual User Sender { get; set; }
        public virtual User Receiver { get; set; }

        public string Content { get; set; }

        public string OriginalCultureInfoEnglishName { get; set; }
        public string ForeignCultureInfoEnglishName { get; set; }
    }

public class Language
    {
        [Key]
        public int LanguageId { get; set; }
        public string CultureInfoEnglishName { get; set; }
    }

Now i made a Service.svc which has my DatabaseContext so it can directly acces my database models. What i want to achieve is that instead of directly getting the database models i would like to get the DTO models when i query against my service.

A Example of how my dto's would look like

public class UserDTO
    {
        public int UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Country { get; set; }
        public string PhoneNumber { get; set; }

        public ICollection<ContactDTO> Contacts { get; set; }

        public virtual LanguageDTO Language { get; set; }

        public UserModel()
        {
            Contacts = new List<ContactDTO>();
        }
    }

public class ContactDTO
    {
        public int UserId { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }

        public string Country { get; set; }
        public string PhoneNumber { get; set; }

        public virtual LanguageDTO Language { get; set; }
    }

public class LanguageDTO
    {
        public int LanguageId { get; set; }
        public string CultureInfoEnglishName { get; set; }
    }

public class MessageDTO
    {
        public int MessageId { get; set; }
        public DateTime SentDate { get; set; }

        public virtual ContactDTO Sender { get; set; }
        public virtual ContactDTO Receiver { get; set; }

        public string Content { get; set; }

        public string OriginalCultureInfoEnglishName { get; set; }
        public string ForeignCultureInfoEnglishName { get; set; }
    }

Now is it possible to do it like this by making a different context that i can use in my service.svc or is there any other way to achieve the this?

for example i would like to get ContactDto by userid which is a user but with less properties because they are not relevant in the client application. I see this happening by a uri http://localhost:54895/Service.svc/ContactDto(1)

Hopefully anyone can clear this up for me because it is really frustrating :)


Solution

  • I'm not sure that what you're interested in is possible, exactly. You are looking to have multiple entity sets per type (aka MEST), and I don't know how well that's supported.

    Beyond that point, and into a discussion around DTOs in general...

    If you use custom providers, you can implement your own IDataServiceMetadataProvider and IDataServiceQueryProvider. When your service starts, you can make calls into the IDataServiceMetadataProvider to control what entities and properties are exposed or hidden -- including exposing properties that do not actually exist on your entity. The upshot is that you end up with a DTO without coding a DTO class. The exposed metadata is the DTO. This is a good resource for creating your own providers.

    In your case, this isn't a 100% solution, because you can't selectively choose when a property is exposed and when it's not.

    Hope this helps...