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

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 :)

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...

Related

How unflattening commands to complex types

I'm not yet dependent to either Mapster or AutoMapper. For now I'm using handwritten mappings because I couldn't find a mapper who could do this with smaller code.
The problem is how do we map flatten structures to complex objects? I think a lot of people could benefit from a good mapping example for such a complex object. I've got even a mapping condition based on CopyOfficeAddressAsInvoiceAddress whether or not the office address needs to be copied as invoice address. I've looked all over the place but couldn't get it to work.
Maybe I should also use a different naming to make it more clear for the mapping algorithm?!
The biggest question could such a map being resolved by a mapper or is this to complex? Al the demo's I've seen were using dto and model objects that are quite similar to each other. I didn't get the point of mapping an object to another object that 99% similar to each other.
I have a Command (I'm using Mediatr) that looks like as follows:
public class Command : IRequest<IActionResult>
{
public string AccountName { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactEMail { get; set; }
public string ContactPhoneNumber { get; set; }
public string BankAccount { get; set; }
public string Bank { get; set; }
public string OfficeName { get; set; }
public string OfficeAddressStreet { get; set; }
public int OfficeAddressStreetNumber { get; set; }
public string? OfficeAddressStreetNumberAddition { get; set; }
public string OfficeAddressPostalcode { get; set; }
public string OfficeAddressCity { get; set; }
public string OfficeAddressCountry { get; set; }
public string? OfficeInvoiceAddressStreet { get; set; } = null;
public int? OfficeInvoiceAddressStreetNumber { get; set; } = null;
public string? OfficeInvoiceAddressStreetNumberAddition { get; set; } = null;
public string? OfficeInvoiceAddressPostalcode { get; set; } = null;
public string? OfficeInvoiceAddressCity { get; set; } = null;
public string? OfficeInvoiceAddressCountry { get; set; } = null;
//[Ignore]
public bool? CopyOfficeAddressAsInvoiceAddress { get; set; } = false;
public string? AssociationIdentifier { get; set; } = null;
}
And I want it to be mapped to the following models:
public class Account
{
public int Id { get; set; }
public string AccountName { get; set; }
public IList<Contact> Users { get; set; }
public IList<Office> Offices { get; set; }
public string Bank { get; set; }
public string BankAccount { get; set; }
public string? AssociationIdentifier { get; set; }
}
public class Office
{
public int Id { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public Address InvoiceAddress { get; set; }
public bool HeadQuarter { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string Postalcode { get; set; }
public int StreetNumber { get; set; }
public string StreetNumberAddition { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string PhoneNumber { get; set; }
}
First of all, my experience is mainly using Automapper, and it is definitely possible to map complex types like this.
But your command does not need to be completely flat. There is nothing inherently wrong with DTOs being similar to your domain models. Using Automapper this is fairly easy as properties with the same name are mapped 1:1.
It could be that you are submitting a form with all the properties flattened in one object. In that case you could define either a seperate map for this object and each domain object.
CreateMap<AccountDto, Account>(); // mapping logic omitted
CreateMap<AccountDto, Office>();
...
Or you could map the one object to a range of objects using Tuples.
CreateMap<AccountDto, (Account, Office, ...)>(); // mapping logic omitted
But if you define seperate DTOs and make mapping profiles for them, it will probably ease your whole mapping experience. For copying the address, you can simply do something like this, in that case.
if (copyAddress)
{
office.InvoiceAddress = _mapper.Map<Address>(addressDto);
}

EF Code First Many One-To-One-Relationships

I'm starting making experience with the EF Code-First and also WCF Service and run into a problem I could not solve with all the guides about this issue:
I got the following code structure
[DataContract]
public class Feed
{
public int Id { get; set; }
public int LanguageId { get; set; }
public int CategoryId { get; set; }
public int TypeId { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public FeedCategory Category { get; set; }
[DataMember]
public FeedType Type { get; set; }
[DataMember]
public string FeedUrl { get; set; }
[DataMember]
public Language Language { get; set; }
}
[DataContract]
public class FeedCategory
{
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public DateTime Registered { get; set; }
[DataMember]
public IList<Feed> Feeds { get; set; }
}
[DataContract]
public class FeedType
{
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public DateTime Registered { get; set; }
public IList<Feed> Feeds { get; set; }
}
[DataContract]
public class Language
{
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string CountryName { get; set; }
[DataMember]
public string CountryCode { get; set; }
[DataMember]
public string ShortCountryCode { get; set; }
}
But when I want to get all the feeds, all dependencies won't be received, so Category, Type & Language is null and I have no idea how to solve it.
Does anyone else knows how to do it?
I'm going to try and answer your question correctly based on my experience (from awhile ago as my company doesn't use EF anymore).
First you may need to apply a key to your entities. I did this in the OnModelCreating method.
modelBuilder.Entity<FeedType>().HasKey(k => k.Id );
Second I believe you have to set the mappings between these entities also able to be done in your OnModelCreating method.
modelBuilder.Entity<FeedType>().HasRequired<Feed>(h => h.Feed).WithOptional(x => x.FeedType);
Finally your need to enable eager loading or use the .Include on your query so that the child object is retrieved when the parent is.
All corrections welcome as it has been awhile.

ASP.NET MVC 5, how to enable validation annotation on viewmodel that composes other view models?

Well I have a very complex User Profile system in a social network application I am building. The profile page has tabs that distinguishes each category of user profile information: Basic, Education, Job. There is a UserProfileViewModel sitting on top of everything, which composes of inner view models such as BasicViewModel, EducationViewModel and JobViewModel. Consider the structure as below:
public class ProfileViewModel
{
public string id { get; set; }
public BasicViewModel basic { get; set; }
public EducationViewModel education { get; set; }
public JobViewModel job { get; set; }
}
public class BasicViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfRegistration { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Gender { get; set; }
public string PhoneNumber { get; set; }
[Display(Name = "Biography")]
public string Biography { get; set; }
public string NickName { get; set; }
public string FavoriteQuotes { get; set; }
}
public class EducationViewModel{
public string EducationStatus { get; set; }
public List<University> Universities { get; set; }
public string CourseStatus { get; set; }
public string CourseSpecialization { get; set; }
public List<string> EducationEvents { get; set; }
}
public class JobViewModel
{
public string WorkStatus { get; set; }
public List<Company> Companies { get; set; }
}
public abstract class Organization
{
public string Name { get; set; }
public DateTime? Year { get; set; }
public int TimePeiod { get; set; }
}
public class University: Organization
{
public string Degree { get; set; }
public string Profession { get; set; }
}
public class Company: Organization
{
public string Website { get; set; }
public string Position { get; set; }
}
So the question is, does data annotation for model validation(both server and client side) work for a model that has composite structure like this? If so, do I just place annotation like I usually do with simple view models? If not, how can I achieve this in alternative ways? Please help.
Any single view model may contain other viewmodels like this:
This model is server side:
[Serializable]
public class MyBigViewModel : IValidatableObject
{
public MyBigViewModel(){
MyOtherViewModel = new MyOtherViewModel();
MyThirdViewModel = new MyThirdViewModel();
}
public MyOtherViewModel {get;set;}
public MyThiddViewModel {get;set;}
public void Post(){
//you can do something here based on post back
//like maybe this where the post method here processes new data
MyOtherViewModel.Post();
}
}
The controller could look like this:
public ActionResult UserList (MyBigViewModel uvm){
if(ModelState.IsValid){
uvm.Post();
return View(uvm);
}
return View(uvm);
}
You can implement the IValidateableObject to do "server side" validation. In the example above however, we want each viewmodel to "contain" it's own model for validation.
Each viewmodel property can use data annotations "contained" in only that viewmodel. It's a very nice way to "Contain" what you want where you want.
I very often use multiple Viewmodels in main VM and pass them in with partial views as needed.

Expanding classes using AutoMap

I have to import a set of data from one database to another with a somewhat different schema, and I'm considering using AutoMap. I could just write a bunch of SQL scripts, but I already have both databases in EF and I want to learn AutoMap ...
While many of the classes are similar, the problem I'm having is where the structure is really different. The target models were designed with several more layers of classes. Instead of flattening, I need to expand.
The target classes have the following properties:
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo Location { get; set; }
public List<Policy> Policies { get; set; }
}
public class ContactInfo
{
public int Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public State State { get; set; }
public string Zip { get; set; }
public string EMail { get; set; }
public List<Phone> PhoneNumbers { get; set; }
}
public class Phone
{
public int Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
public class Policy
{
public int Id { get; set; }
public PolicyNumber PolicyNumber { get; set; }
public List<Transaction> Transactions { get; set; }
}
The source tables, however, are relatively flattened.
public partial class Account
{
public string AccountId { get; set; }
public string AccountName { get; set; }
public string PolicyNumber { get; set; }
}
public partial class Transaction
{
public int TransactionId { get; set; }
public int AccountId { get; set; }
public Nullable<System.DateTime> EffectiveDate { get; set; }
public string InsuredName { get; set; }
public string InsuredAddress { get; set; }
public string InsuredCity { get; set; }
public string InsuredState { get; set; }
public string InsuredZip { get; set; }
public string InsuredPhone { get; set; }
}
I can create the Map, but I don't know how to tell AutoMapper to handle converting the string Policy to a policy object and then add it to the list of Policies.
Mapper.CreateMap<Source.Account, Destination.Account>();
Even worse, the source data inexplicitly has the name and address info at the transaction level. Before you tell me that AutoMap might not be the best solution, please understand that these two source tables are 2 out of over 40 tables in this database, and that the others are not nearly as troublesome.
Can I configure AutoMap to convert the string property PolicyNumber to a Policy Object and add it to the Policies List of the target class?
Any suggestions on how I can get the name and address information from the Transaction into a ContactInfo class and add it at the Account level?
Thank you.
Thanks to Thomas Weller. Custom Value Resolvers handled exactly what I needed.

Correct use of Object Properties

Below is a Class I created to track the current Person in my glorified Data Entry and retrieval app. Once they select a person it calls the constrtuctor which then calls the database to fill in all the rest of the info. Also, throughout the program they will be able to change the various fields.
With this in mind do I have the below set up correctly? I am inexpierenced with properties and using Objects to store data across multiple forms and would appreciate any insight.
Thanks!
class CurrentPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string SuffixID { get; set; }
public string TitleID { get; set; }
public string SocialSn { get; set; }
public string BirthDate { get; set; }
public string Gender { get; set; }
public string DlNumber { get; set; }
public string DlStateID { get; set; }
public string PrimaryRace { get; set; }
public string SecondaryRace { get; set; }
public string EmailAddress { get; set; }
public string MaritalStatus { get; set; }
public string InsertProgram { get; set; }
public string InsertUserID { get; set; }
public string UpdateProgram { get; set; }
public string UpdateUserID { get; set; }
public string LockID { get; set; }
public int PersonID { get; set; }
public int ClientID { get; set; }
public int ResidencyCountyID { get; set; }
public int ResponsibilityCountyID { get; set; }
public bool HispanicOriginFlag { get; set; }
public bool CitizenFlag { get; set; }
public bool VeteranFlag { get; set; }
public DateTime DeathDate { get; set; }
public DateTime InsertDateTime { get; set; }
public DateTime UpdateDateTime { get; set; }
// Put the default Constructor back in
public CurrentPerson(){}
// Custom Constructor that needs the PersonID
public CurrentPerson(int pID)
{
PersonID = pID;
// Methods to get rest of data here
}
}
yup, looks good.
you can, btw, set access on the get/set as well, to make it read/write only publicly
public DateTime DeathDate
{
get;
private set;
}
This is technically fine. They are all declared perfectly well.
However, often, with DB apps, you'll want to not use automatic properties, since property setters are often a great place to do some validation, as well as potentially marking properties/objects as "dirty" and requiring saving of some sort.
The auto property is always get and set, so that you have no control about properties set (to mark the instance as dirty, or whatever). Therefore, while this is an acceptable class as data entity only, I usually find that auto properties are only rarely really applicable.

Categories