I am working on a service oriented architecture. I have 3 tables Meeting, Stakeholder and MeetingStakeholder (a junction table).
A simple representation of POCO classes for all 3 tables:
public class Meeting
{
public int Id { get; set; }
public IList<MeetingStakeholder> MeetingStakeholders { get; set; }
}
public class Stakeholder
{
public int Id { get; set; }
}
public class MeetingStakeholder
{
public int Id { get; set; }
public int MeetingId { get; set; }
public Meeting Meeting { get; set; }
public int StakeholderId { get; set; }
public Stakeholder Stakeholder { get; set; }
}
A simple representation of Meeting Dto:
public class MeetingDto
{
public int Id { get; set; }
public IList<int> StakeholderIds { get; set; }
}
In PUT action,
PUT: api/meetings/1
First I removes all existing records from MeetingStakeholder (junction table) then prepares new List<MeetingStakeholder> meetingStakeholders using meetingDto.StakeholderIds and create it.
{
List<MeetingStakeholder> existingMeetingStakeholders = _unitOfWork.MeetingStakeholderRepository.Where(x=> x.MeetingId == meetingDto.Id);
_unitOfWork.MeetingStakeholderRepository.RemoveRange(existingMeetingStakeholders);
List<MeetingStakeholder> meetingStakeholders = ... ;
_unitOfWork.MeetingRepository.Update(meeting);
_unitOfWork.MeetingStakeholderRepository.CreateRange(meetingStakeholders);
_unitOfWork.SaveChanges();
return OK(meetingDto);
}
Everything is fine to me. But my architect told me that i am doing wrong thing.
He said, in PUT action (according to SRP) I should not be removing and re-creating MeetingStakeholder records, I should be responsible for updating meeting object only.
According to him, MeetingStakeholderIds (array of integers) should be send in request body to these routes.
For assigning new stakeholders to meeting.
POST: api/meetings/1/stakeholders
For removing existing stakeholders from meeting.
Delete: api/meetings/1/stakeholders
But the problem is, In meeting edit screen my front-end developer uses multi-select for Stakeholders. He will need to maintain two Arrays of integers.
First Array for those stakeholders Ids which end-user unselect from multi-select.
Second Array for new newly selected stakeholders Ids.
Then he will send these two arrays to their respective routes as I mentioned above.
If my architect is right then I have no problem but how should my front-end developer handle stakeholders selection in edit screen?
One thing I want to clarify that my junction table is very simple, it does not contain additional columns other than MeetingId and StakeholderId ( a very basic junction). So in this scenario, does it make sense to create separate POST/DELETE actions on "api/meetings/1/stakeholders" that receives StakeholderIds (list of integers) instead of receiving StakeholderIds directly in MeetingDto??
First of all, if I am not mistaken:
you have a resource: "Meeting";
you want to update the said resource (using HTTP/PUT).
So updating a meeting by requesting a PUT on "/api/meetings/:id" seems fairly simple, concise, direct and clear. All good traits for designing a good interface. And it still respects the Single Responsibility Principle: You are updating a resource"
Nonetheless, I also agree with you architect in providing, in addition to the previous method, POST/Delete actions on "api/meetings/1/stakeholders" if the requisites justify so. We should be pragmatic at some level not to overengineer something that isn't required to.
Now if your architect just said that because of HOW IT IS PERSISTED, then he is wrong. Interfaces should be clear to the end user (frontend today, another service or app tomorrow ...), but most importantly, in this case, ignorant of its persistence or any implementation for that matter.
Your api should focus on your domain and your business rules, not on how you store the information.
This is just my view. If someone does not agree with me I would like to be called out and so both could grow and learn together.
:) Hope I Could be of some help. Cheers
Related
My project is an online foods order app, the key feature of this app is the "Daily nutrients intake monitor". This monitor shows the differences of daily intake recommendation values of 30 types of nutrients vs the actual nutrients contains from the foods in user's shoppingcart.
I created 30 models base on those nutrients and each one of them has an InputData which inherits from a base class - NutrientInputDataBase, below is the example of Added sugar InputData class and the base class:
public class AddedSugarUlInputData : NutrientInputDataBase
{
[ColumnName(#"AddedSugar-AMDR-UL")]
public float AddedSugar_AMDR_UL { get; set; }
}
public class NutrientInputDataBase
{
[ColumnName(#"Sex")]
public float Sex { get; set; }
[ColumnName(#"Age")]
public float Age { get; set; }
[ColumnName(#"Activity")]
public float Activity { get; set; }
[ColumnName(#"BMI")]
public float BMI { get; set; }
[ColumnName(#"Disease")]
public float Disease { get; set; }
}
From the official documents:
https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/serve-model-web-api-ml-net
i understood that i need to create a 'PredictionEnginePool' and i already know how to register the PredictionEnginePool in the application startup file.
My app logic is when user added or removed an item from the shoppingcart, the front end will request the api, the backend will get the user profile first(to obtain the input data for the prediction), then return a packaged objects which contains all 30 types of nutrients prediction results.
My question is, should i register the PredictionEnginePool for each one of the nutrient model individually in the Startup file? or in anyother effecient way which i haven't be awared of?
There's multiple ways for you to go about it.
Register each of your models PredictionEnginePool. The FromFile and FromUri methods allow you to specify a name for each of your models so when you use them to make predictions in your application you can reference them by name.
Save your model to a database as a blob. Then you can add logic on your application to load a specific model based on the criteria you specify. The downside to this is you'd have to fetch your models more dynamically rather than having a PredictionEnginePool ready to go.
i'm writing a system to track observation values from sensors (e.g. temperature, wind direction and speed) at different sites. I'm writing it in C# (within VS2015) using a code-first approach. Although i've a reasonable amount of programming experience, I'm relatively new to C# and the code-first approach.
I've defined my classes as below. I've built a REST api to accept observation reading through Post, which has driven my desire to have Sensor keyed by a string rather than an integer - Some sensors have their own unique identifier built in. Otherwise, i'm trying to follow the Microsoft Contoso university example (instructors - courses- enrolments).
What I am trying to achieve is a page for a specific site with a list of the sensors at the site, and their readings. Eventually this page will present the data in graphical form. But for now, i'm just after the raw data.
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Sensor> Sensors { get; set; }
}
public class Sensor
{
[Key]
public string SensorName { get; set; }
public int SensorTypeId { get; set; }
public int SiteId { get; set; }
public ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int Id { get; set; }
public string SensorName { get; set; }
public float ObsValue { get; set; }
public DateTime ObsDateTime { get; set; }
}
and I've created a View Model for the page I'm going to use...
public class SiteDataViewModel
{
public Site Site { get; set; }
public IEnumerable<Sensor> Sensors { get; set;}
public IEnumerable<Observation> Observations { get; set; }
}
and then i try to join up the 3 classes into that View Model in the SiteController.cs...
public actionresult Details()
var viewModel.Site = _context.Sites
.Include(i => i.Sensors.select(c => c.Observations));
i used to get an error about "cannot convert lambda expression to type string", but then I included "using System.Data.Entity;" and the error has changed to two errors... on the 'include', I get "cannot resolve method 'include(lambda expression)'...". And on the 'select' i get "Icollection does not include a definition for select..."
There's probably all sorts of nastiness going on, but if someone could explain where the errors are (and more importantly why they are errors), then I'd be extremely grateful.
Simply you can you use like
viewModel.Site = _context.Sites
.Include("Sensors).Include("Sensors.Observations");
Hope this helps.
The way your ViewModel is setup, you're going to have 3 unrelated sets of data. Sites, sensors, and observations. Sites will have no inherent relation to sensors -- you'll have to manually match them on the foreign key. Realistically, your ViewModel should just be a list of Sites. You want to do
#Model.Sites[0].Sensors[0].Observations[0]
not something convoluted like
var site = #Model.Sites[0]; var sensor = #Model.Sensors.Where(s => SiteId == site.Id).Single(); etc...
Try doing
viewModel.Site = _context.Sites.Include("Sensors.Observations").ToList();
Eager-loading multiple levels of EF Relations can be accomplished in just one line.
One of the errors you reported receiving, by the way, is because you're using 'select' instead of 'Select'
And lastly, be aware that eager-loading like this can produce a huge amount of in-memory data. Consider splitting up your calls for each relation, such that you display a list of Sensors, and clicking, say, a dropdown will call an API that retrieves a list of Sites, etc. This is a bit more streamlined, and it prevents you from getting held up because your page is loading so much information.
Update
I've created a sample application for you that you can browse and look through. Data is populated in the Startup.Configure method, and retrieved in the About.cshtml.cs file and the About.cshtml page.. This produces this page, which is what you're looking for I believe.
I'm trying work out the best way to structure our API; we have Reviews which we've setup in a standard REST structure (list one, list all, create, update etc). Where it doesn't quite fit the examples is: each review can be linked to one or more other types e.g. Event, Location or Thing.
My thinking is the urls would be along the lines of:
/event/reviews/ (or the reverse of this e.g. /reviews/event/)
/location/reviews/
/thing/reviews/
The issue I can see however is the "GET" for each of these should return the parent object i.e. an Event.
So using ServiceStack, what's the best way to handle this scenario? Is it to create a custom service for each data request rather than abusing the out-of-the-box REST setup or have I missed something more fundamental?
Firstly "Best" solution is a fairly subjective term. I'll generally aim for DRY, re-usable, performant solutions that promotes the least effort, friction and chattiness, whilst others may define "Best" in how closely it follows the principles of REST. So you will get varied responses depending on what the goals are. I can only offer how I would approach it.
ServiceStack service implementations are de-coupled from their custom routes
One thing to keep in mind is how you define and design your services in ServiceStack are fairly de-coupled in how you expose them, since you can expose your services under any custom route. ServiceStack encourages a message-based design so you should give each operation a distinct message.
Use a logical / hierarchical Url structure
I'd use a logical Url structure that I aim to represent the identifier of a noun, which is hierarchically structured, i.e. the parent path categorizes your resource and gives it meaningful context. So in this case if you wanted to expose Events and reviews my inclination is to go with following url structure:
/events //all events
/events/1 //event #1
/events/1/reviews //event #1 reviews
Each of these resource identifiers can have any HTTP Verb applied to them
Implementation
For the implementation I generally follow a message-based design and group all related operations based on Response type and call context. For this I would do something like:
[Route("/events", "GET")]
[Route("/events/category/{Category}", "GET")] //*Optional top-level views
public class SearchEvents : IReturn<SearchEventsResponse>
{
//Optional resultset filters, e.g. ?Category=Tech&Query=servicestack
public string Category { get; set; }
public string Query { get; set; }
}
[Route("/events", "POST")]
public class CreateEvent : IReturn<Event>
{
public string Name { get; set; }
public DateTime StartDate { get; set; }
}
[Route("/events/{Id}", "GET")]
[Route("/events/code/{EventCode}", "GET")] //*Optional
public class GetEvent : IReturn<Event>
{
public int Id { get; set; }
public string EventCode { get; set; } //Alternative way to fetch an Event
}
[Route("/events/{Id}", "PUT")]
public class UpdateEvent : IReturn<Event>
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
}
And follow a similar pattern for Event reviews
[Route("/events/{EventId}/reviews", "GET")]
public class GetEventReviews : IReturn<GetEventReviewsResponse>
{
public int EventId { get; set; }
}
[Route("/events/{EventId}/reviews/{Id}", "GET")]
public class GetEventReview : IReturn<EventReview>
{
public int EventId { get; set; }
public int Id { get; set; }
}
[Route("/events/{EventId}/reviews", "POST")]
public class CreateEventReview : IReturn<EventReview>
{
public int EventId { get; set; }
public string Comments { get; set; }
}
The implementation should be fairly straight forward based on these messages, which (depending on code-base size) I would organize in 2 EventsService and EventReviewsService classes. I should note that I use pluralization for Service Request DTO names myself to avoid clashing with data models of the same name.
Although I've separated UpdateEvent and CreateEvent here, I will sometimes will merge them into a single idempotent StoreEvent operation if the use-case permits.
Physical Project Structure
Ideally the root-level AppHost project should be kept lightweight and implementation-free. Although for small projects with only a few services it's ok for everything to be in a single project and to simply grow your architecture when and as needed.
For medium-to-large projects we recommend the physical structure below which for the purposes of this example we'll assume our Application is called EventMan.
The order of the projects also show its dependencies, e.g. the top-level EventMan project references all sub projects whilst the last EventMan.ServiceModel project references none:
- EventMan
AppHost.cs // ServiceStack ASP.NET Web or Console Host Project
- EventMan.ServiceInterface // Service implementations (akin to MVC Controllers)
EventsService.cs
EventsReviewsService.cs
- EventMan.Logic //For larger projs: pure C# logic, data models, etc
IGoogleCalendarGateway //E.g of a external dependency this project could use
- EventMan.ServiceModel //Service Request/Response DTOs and DTO types
Events.cs //SearchEvents, CreateEvent, GetEvent DTOs
EventReviews.cs //GetEventReviews, CreateEventReview
Types/
Event.cs //Event type
EventReview.cs //EventReview type
With the EventMan.ServiceModel DTO's kept in their own separate implementation and dependency-free dll, you're freely able to share this dll in any .NET client project as-is - which you can use with any of the generic C# Service Clients to provide an end-to-end typed API without any code-gen.
Update
This recommended project structure is now contained in all ServiceStackVS' VS.NET Templates.
The Simple Customer REST Example has a small self-contained, real-world example of creating a simple REST Service utilizing an RDBMS.
I am creating a web application that manages documents. These documents have stages. Users will be able to reject these documents from the current stage back to the previous stage.
So the flow will be like this Document stage one approved > Get next stage and set document stage to next stage > Document stage one REJECTED > Get previous stage and set document stage to previous stage.
Now what I need help with is how to manage the stages back and forth and what is the best way to setup my entities?
Example Entities
public class Document
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Stage Stage { get; set; }
}
public class Stage
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
Use an enum
Replace your class Stage with an Enum
public enum Stage
{
Rejected, None, Approved, Etc
}
In your NHibernate mapping simplay add the Stage enum to your map
<property name="Stage"></property>
In your db you can simply create the Stage column to an int32 and Nhibernate will figure out how to persist and load the enum automagically.
The advantage of using an enum is that you can always cast the enum to an int and decrement or increment to get the previous or next stage (assuming that you are simply adding them in 0..N).
Stage nextStage = (Stage)(((int)currentDocument.Stage)++);
Stage previousStage = (Stage)(((int)currentDocument.Stage)--);
Otherwise you can use a linq query to get the previous or next steps.
Edit
So far in your requirements you haven't listed that you need anything of the complexity of a generic workflow. Here is a sample app which uses WWF with a document approval system similiar to what you require.
http://www.codeproject.com/KB/WF/wwf_basics_files.aspx
Until you actually need something of the WWF complexity. I would recommend that you use the enum and then refactor when your requirements change. This way you're not implementing a feature "just in case".
This is the 3rd major edit to this question, so I'm going to write a quick little summary first, then ask the question.
I have an input/edit model I'm planning on using with an EF4-backed MVC 2 site. The model is as follows:
public class AdminGameEditModel
{
[Required]
public int GameID { get; set; }
[Required(ErrorMessage="A game must have a title")]
public string GameTitle { get; set; }
[Required(ErrorMessage="A short URL must be supplied")]
public string ShortURL { get; set; }
[Required(ErrorMessage="A box art image must be supplied")]
public HttpPostedFileBase BoxArt { get; set; }
[Required(ErrorMessage="A large image for the index page is required")]
public HttpPostedFileBase IndexImage { get; set; }
[Required(ErrorMessage="A game must have a review")]
public string ReviewText { get; set; }
[Required(ErrorMessage="A game must have a score")]
public int ReviewScore { get; set; }
[Required(ErrorMessage="A game must have at least one Pro listed")]
public string[] Pros { get; set; }
[Required(ErrorMessage="A game must have at least one Con listed")]
public string[] Cons { get; set; }
[Required(ErrorMessage="A game must belong to a genre")]
public int GenreID { get; set; }
[Required(ErrorMessage="A game must be associated with at least one platform")]
public int[] PlatformIDs { get; set; }
}
I'd like to map it to a Game entity for creation/updating. There's a snag, though - I need to save the images in a particular folder, and then take their paths and save those as properties in my entity. So, an example for clarity's sake: rather than my Game entity having a actual BoxArt image, it would instead have the path to the correct BoxArt image. I hope this makes sense. Let me know if I need to clarify.
Can I do this with AutoMapper? If so, can anyone provide some code guidance?
EDIT:
Part of the problem is that my model is fairly complex, as it contains a many-to-many relationship. The PlatformIDs are ultimately used to build/rebuild (depending whether I'm creating or updating an entity) linked Platform entities in the Game/Platform map. I'm not sure if AutoMapper can do something that complex without needing to go through my repository.
Then there's the problem of the image paths. The paths aren't a property of HttpPostedFileBase, but must be constructed by hand like so:
if (BoxArt.ContentLength > 0) {
var fileName = Path.GetFileName(BoxArt.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Images/BoxArt"), fileName);
BoxArt.SaveAs(path);
}
So, what I'm looking for is more complex than just trying to map simple properties across objects. I'd like to keep a reference to my edit model out of my repository. Separation of concerns, and all that. Because of that, I need to map to an entity before I attempt to pass it to my repo for saving. I'm just not sure how to do it without blending app layers.
If I understand you, all you need to do is update your properties first prior to the call to AutoMapper.
Make sure your object has the correct values prior to calling AutoMapper.
After the call to do the mapping, your destination object will have all the matching properties copied over.
Post some more code if this doesn't answer the questoin.