So heres the situation. I've been thinking through how to do this. But I'm not super sure the best approach.
We have two data model classes that are used to create or update another data model class. Lets call them CreateUser and UpdateUser. and those are used to create/update the User class.
public class CreateUser
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser
{
public int userId { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? email { get; set; }
public string? fullName { get; set; }
}
I want the model from UpdateUser to share every attribute from the CreateUser model. But I want those attributes to be nullable. This is so you can do differential updates. This may seem trivial with the classes I'm providing. But in my use case we have very large classes and differential updates will perform better.
Currently we are maintaining these two classes separately. So when you make a change to CreateUser, You have to also go tweak UpdateUser as well. I would like to know if theres a better approach to this.
I've considered interfaces as a good option for this. But the problem is that I cannot define an interface with properties that can be nullable. They either are nullable or they arent. And I dont want my properties in CreateUser to be nullable. Those are required properties
public interface ICreateOrUpdateUser
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
in my example an interface like this would work. Because strings are nullable by default. But if we used an int instead, It breaks.
What I really need is the ability to create a blueprint for two classes that can be either nullable or not nullable.
In C#, strings are nullable (as you stated), so string? is redundant, but for the int case, there are a few options.
https://learn.microsoft.com/en-us/dotnet/api/system.string?view=net-6.0#Nulls
If you're trying to identify a state based model change (for instance, a create user model is different than all other states), you can document that in code by separating the interfaces for the states. This may be a better way of creating self-documenting code.
public interface IUser : IUserInitialization
{
int? userId {get;set;}
}
public interface IUserInitialization
{
string username { get; set; }
string password { get; set; }
string email { get; set; }
string fullName { get; set; }
}
public class CreateUser : IUserInitialization
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser : IUser
{
public int userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
I would recommend removing the nullable if possible, if you want to simplify, the following would match your case. In your CreateUser model, you can change the behaviour for the userId to always be null.
public interface IUser
{
int? userId {get;set;}
string username { get; set; }
string password { get; set; }
string email { get; set; }
string fullName { get; set; }
}
public class CreateUser : IUser
{
public int? userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser : IUser
{
public int? userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
Related
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);
}
I have these two tables and the bridge between them:
public class User
{
[Required]
public string Email { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public int Id { get; set; }
public ICollection<UserLocation> UserLocations { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Username { get; set; }
}
public class Location
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Picture { get; set; }
public string PostCode { get; set; }
public string Region { get; set; }
public ICollection<UserLocation> UserLocations { get; set; }
}
public class UserLocation
{
public Location Location { get; set; }
public int LocationId { get; set; }
public User User { get; set; }
public int UserId { get; set; }
}
I need to create a method that takes as a parameter a search field and a user id, returns all locations realted to that user id and searches if the string is contained in any of the locations' props.
I am having issues returning the locations based on user id.
I tried _context.UserLocations.Include(ul=>ul.Location).Where(l=>l.UserId==userId)
but that didn't work since I got a syntax error when trying to use l.UserId.
I also tried the other way around, _context.Locations.Include(l=>l.UserLocations) but ran into the same issue.
I need to find a way to retrieve all locations related to the user. The search can be easily done using the Contains() method afterwards.
Try this
_context.UserLocations.Where(x => x.UserId == userId).Select(x => x.Location)
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.
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...
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.