I am creating a Web API 2 application based on Entity Framework. My questions are about good practices involved in creating Web API 2 & EF based system.
Suppose that I have class Payment containing class Address.
public class Payment()
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public Address PaymentAddress{ get; set; }
}
public class Address ()
{
public string StreetName{ get; set; }
public string City{ get; set; }
}
Question 1:
Does my Web API should allow for changing Address from endpoint PUT /payments (I mean changing Address's properties not the whole object) called Solution 1
Question 2:
or maybe PUT /payments should update only simple properties of Payment without Address properties? In this scenario Address properties could by changed by endpoint PUT /payments/{paymentId}/address (called Solution 2)?
Question 3:
Currently I have implemented Solution 1 but I have problem to set context properly for Payment updates with updated Address's properties. I have to manually change EF State value of Address. If solution 2 would solve my problem?
Question 4
By default EF one-to-many relation is defined automatically like in my example. But I can use ForeignKey attribute mark column as foreign key. Why this solution is better? Does is help in anything?
public class Payment()
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public int AddressId{ get; set; }
[ForeignKey("AddressId")]
public Address PaymentAddress{ get; set; }
}
Related
I am new to full stack software development, and I have an assignment to create a simple full stack web app in .NET Core, Entity Framework Core (when it comes to the back-end part), which would store some employee data. Now, let's say I have an Employee entity. We also need to implement the Employee's position in some way (an employee can have the position of a back-end developer, front-end developer, project manager etc.). That's why I have also created this EmployeePosition class, so that there is an EmployeePosition property in Employee. I also have other similar status enitities like the EmployeePosition in the app.
EmployeePosition would then be seeded to a separate table in the database, and thus it can be applicable to any company that "would use" this app (just use different seed data). I will use a legacy database for seeding.
Now it seems that this would work, but it also seems a bit too crude for me, because whenever I would need to do an operation with the status in the repository, I would have the use the magic Id number of the status, or hardcode the status Name property somewhere in a method (if I don't have a smarter workaround for the given operation), for instance "Switch(Position.Name) case "front-end": do stuff" etc.
Could I maybe get a suggestion or direction on how to implement this better, am I on a good track here?
Would it be better to actually use enums as data types inside of EmployeePosition, any suggestions on how to implement that? Considering that I have to use the legacy database for seeding, I would have to somehow create my enums out of the legacy file.
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Image { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public EmployeePosition Position { get; set; }
public class EmployeePosition
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
What you can do is create an Enum for employee positions.
public enum EmployeePosition
{
Backend = 0,
Frontend = 1,
DBA = 2,
}
And your employee entity
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Image { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public EmployeePosition Position { get; set; }
}
Then you can use
If (Employee.Position == EmployeePosition.Backend)
{
// your logic.
}
You have conflicting requirements unfortunately. You say that your program is "status-agnostic":
EmployeePosition would then be seeded to a separate table in the database, and thus it can be applicable to any company that "would use" this app (just use different seed data).
And yet, it's really not:
Because whenever I would need to do an operation with the status in the repository, I would have the use the magic Id number of the status, or hardcode the status Name property somewhere in a method (if I don't have a smarter workaround for the given operation), for instance "Switch(Position.Name) case "front-end": do stuff" etc.
So one of those requirements has to "win". Either your code knows what the different statuses are (and so different seed data wouldn't work) or it doesn't.
If the first requirement is more important:
Keep your code as it is, you have a perfectly reasonable model for N number of statuses where they aren't known ahead of time, nor is any logic specific to a given entry
If the second requirement is more important:
Switch to an enum. You don't want magic numbers or strings floating around in your code. As you mention, you'll need to map each existing data row into one of the predefined enum members.
I am creating some view models for my ASP MVC web app.
I created "code first" models for database. Is it a good way to derive view models from database models?
Example database model:
public class Project
{
[Key]
public int Id { get; set; }
public int? CustomerId { get; set; }
public int TypeId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
View model:
public class ViewModelProject : Project
{
[NotMapped]
public DateTime? Start { get; set; }
[NotMapped]
public DateTime? End { get; set; }
[NotMapped]
public string Manager { get; set; }
}
Is this the right way or is it completely false?
EDIT (subquestion):
I have some very simple database models like ProjectType, which only contains i.e. two properties. Should I also fragment those models in model view or can I make it that way:
Simple database model:
public class ProjectType
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int? Code { get; set; }
}
Can I use it like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public ProjectType Type { get; set; }
}
Or does it have to be fragmented like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int TypeCode { get; set; }
}
I would not recommend doing it this way. I (and many others) have tried it and it doesn't work well. You will inadvertedly run into troubles, since an MVC model has to be tailored to the view and what you get from the DB rarely fits. Sure, you can hammer it into place, but the code quickly gets messy and store-related and UI code starts to mangle together. This even shows in your example, since you have to put the NotMappedAttribute (which is related to data storage), to ViewModelProject (a class at UI level).
There are many other examples to show this problem, but an especially good one I find when you want to serialize a model object to JSON and send it to a JavaScript client. The JSON serializer takes the values of all public properties and adds them to the JSON. If you want to exclude a property, you have to mark it with a ScriptIgnoreAttribute, which you would also have to apply to the base class, which breaks separation between UI and store-related code.
The better way to go is to keep the staorage model and the MVC model separated and to map the data from one to the other (there are already pre-existing frameworks that help you with that, such as Automapper). This comes with additional advantages, for example better testability, since you are now not dependent on a specific data store to create model instances.
I'm very new to entity framework and web api so please excuse me if what I'm trying to do doesn't make much sense! Is it possible to send one model on a post and receive a different model on a get? My example.
I have a very simple companies model:
public class Company
{
[Required]
public string companyName { get; set; }
public virtual List<Branch> branches { get; set; }
}
public class Branch
{
public int companyID { get; set; }
public string branchName { get; set; }
public string address { get; set; }
public string postcode { get; set;}
public string phoneNumber { get; set; }
public virtual Company company { get; set; }
}
When I post to myapp/api/companies I want to be able to include a list of branches to add alongside the company, this is currently working perfectly however, my front end designer has requested that the companies come back without the branches attached. I have tried [jsonIgnore] against the branches and this works for the GET but it also stops the branches from writing on a post. Is there some way to apply the jsonIgnore on the controller perhaps?
Should I try and convince my front-end guy to just ignore the branch data or is there some way I could omit it in the response?
Thanks
Chris
The way I finally managed to do this was to use eager loading instead of lazy loading. To achieve that remove virtual from each of the relations. This stops EF from getting the relations automatically then in any code that returns the object use .Include() to include any relations you want back in that return.
I'm currently learning about various application design approaches and there's one thing I can't really get my head around.
Let's say I have a physical device in my network, an IP surveillance camera. I need a model that represents 'IP surveillance cameras' to work with them in my application, which might look like this:
public class IPCamera {
public string Token { get; set; }
public IPAddress IPAddress { get; set; }
public string RtspUri { get; set; }
}
Now, if I want to store the IPCamera into a database (e.g. with Entity Framework), I need maybe other/additional properties and thus, another model with:
public int Id { get; set; }
Oh.. I want to access my application with via a WCF service. I can't use the object "IPAddress" here because it's not very serialization friendly, hence I need another model with a custom IP class that stores the IP address as string.
And last, an Android clients wants to access my data. I design a RESTful API using WebApi2. The client isn't interested in the cameras RTSPUri, but wants to know about the current state (is online, is offline).
I'm wondering: How to name all these models that they don't come in conflict? Should I design a model per each purpose/layer/service? And how to link/map them? With the adapter pattern?
I would include everything in your entity and then create view models that only expose the properties that matter to the domain you're accessing your entities through.
Your entity:
public class IpCamera
{
public int Id { get; set; }
public string Token { get; set; }
public IPAddress IPAddress { get; set; }
public string RtspUri { get; set; }
public bool IsOnline { get; set; }
}
In your WCF service:
public class IpCameraViewModel
{
public int Id { get; set; }
public string IpAddress { get; set; }
public string Token { get; set; }
public string RtspUri { get; set; }
}
In your api project:
public class IpCameraViewModel
{
public int Id { get; set; }
public string IpAddress { get; set; }
public string Token { get; set; }
public bool IsOnline { get; set; }
}
And you can just set the IpAddress as a string to send to a receiving client. You can shed away any properties you don't want to expose. Or you can add properties that don't belong to the IpCamera entity and just add them to your view model from another entity.
As #Smith.h.Neil suggested, you should create one base normalized model (entity if you will) to store and several view models (projections).
You can easily map to/from view models using tools like AutoMapper.
As for the naming, I wouldn't use technical suffixes (like *ViewModel) in API layer. Think hard and figure out correct domain (business oriented) name of each projection. Like IpCameraStatus or perhaps just IpCamera (but in another namespace).
I'm an EF noob (any version) and my Google-foo has failed me on finding out how to do this. Which makes me think I must be doing this wrong, but here is the situation:
I'm definitely in an environment that is database first and the schema won't be updated by us coders. I'm also not a fan of 'automatic' code generation, so I've stayed away from the designer or the EF powertools (though I did run through them just to see them work).
To learn I imported the Northwind DB into my LocalDB to have something to play with while creating some simple Web API 2 endpoints. This all went well as I created slimmed down models of the Employees, Shippers, & Region tables in Northwind. Region was particularly interesting as it wasn't plural and EF had issues with that. Anyway, I got by that.
My trouble now is; I want to use a view instead of a table as my source and whatever I'm doing just doesn't seem to work. What I tried was setting it up just like I did the tables. But that produces a ModelValidationException error. I tried looking at the auto-generated code from the designer, but got no insight.
My models:
//-- employee, shipper, & region work as expected
public class employee {
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
public class shipper {
public int ShipperID { get; set; }
public string CompanyName { get; set; }
public string Phone { get; set; }
}
public class region {
public int RegionID { get; set; }
public string RegionDescription { get; set; }
}
//-- invoice is a view (actual viewname is 'Invoices')
//-- so i followed the same rules as i did for employee & shipper
//-- i have tried uppercase 'I' as well as a plural version of the model
public class invoice {
public string CustomerID { get; set; }
public string CustomerName { get; set; }
public string Salesperson { get; set; }
public int OrderID { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
}
My Context looks like this:
public class NorthwindDBContext : DbContext {
public DbSet<Employee> Employees { get; set; }
public DbSet<shipper> Shippers { get; set; }
public DbSet<region> Regions { get; set; }
public DbSet<Invoice> Invoices { get; set; } //-- offending line of code
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//--- fix for Region being singular instead of plural
modelBuilder.Entity<region>().ToTable("Region");
}
}
If I comment out the public DbSet<Invoice> Invoices { get; set; } line in the context everything works. Just by having the line present (even if i don't reference the Invoices property) I receive the ModelValidationException error when using the context in anyway.
Can anybody tell me what I'm doing wrong here?
Thanks.
Update: I tried this in one of my controllers, but I am too noob'ish to know if this is the right path either, though it worked as far as getting records.
using (var dbContext = new NorthwindDBContext()) {
return dbContext.Database.SqlQuery<Invoice>("select * from invoices").ToList();
}
Code-first conventions will look for an ID or InvoiceID property to use as a key. Your Invoice model has neither, while the others do. This is the specific reason your code is failing.
The less-specific one is that you can't have entities in EF which lack a unique key. If you can, have the view define a key. Otherwise, you may still be able to work around the issue.