I am trying to refactor a solution to bring on board another project.
I have a Core project where common classes across projects reside.
I've tried to simpify my question by using 2 imaginary projects: Holidays and Weather...
I have a file load process setup for the Holidays project which has the following 2 classes:
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
public virtual Security Security { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
public class File
{
public virtual string FileName { get; set; }
public virtual FileType FileType { get; set; }
public virtual FileStatusType FileStatusType { get; set; }
public virtual Job Job { get; set; }
}
The file load process for the Weather project has exactly the same structure as Holidays, except that the Jobs class does not have a Security property.
My question is, is it possible to somehow move both classes into the Core project to allow both projects to use them?
Obviously Weather does not need the Security property, so I was thinking I would have a Core.Job class without Security, and then extend the Core.Job in Holidays.Job.
But once I do that, in the Core.File class, what Job is it referring to? As it sits in the Core project it must be the Core.Job.
So would I then need to have Job and File sit in Holidays, and Weather (and any other future projects) use the Core.Job and Core.File?
I don't want the Core project to have any references to sub projects.
I am using NHibernate, and so have mapping files - adding to the complexity.
Hope this is clear enough
Thanks
You can certainly do this, but I am not sure whether it brings you true benefit:
Does the Core itself work with the base Job in any way? If it does not, implementing Job separately in each project may help you keep coupling loose, even though I'd a little redundant. In code I wrote, I have sometimes introduced unnecessary dependencies by extracting interfaces without adding true benefit. This is why I am a bit precautious.
In case Core does acutal work with it, the part to refactor into the common base Job is perhaps the interface it works with.
You may think of an interface instead of a base class. Security may semantically belong to another interface. Moreover, you hand over a lot of control over your classes to the Core.
Do you ever hand a job from one project to another (or are they mapped to the same DB table via NHibernate?)? If you don't, an internal redundant class may be fine too.
Not very clear why confuse on the soluton offered by you (assuming that I right understood you)
//Core DLL
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
in the Hollidays you have
public class HollidayJob : Job
{
public virtual Security Security { get; set; }
}
in Weather simply use a type Job, if it selfsufficient.
In this case you refer CoreDLL from Holliday project and Weather. When you serialize it via NHibernate it for HollidayJob save one field more, but when Weather reads the same table it skips that field, as don't know anything, and don't actually care abotu it.
Hope this helps.
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.
So, we've created a interview. It is built something like this:
class interview
{
public virtual ICollection<WebQuestionPage> WebQuestionPage { get; set; }
}
Then, in the WebQuestionPage class we have
public virtual ICollection<WebQuestionSection> WebQuestionSection { get; set; }
sections!
And each section might contain another, nested, section and/or questions
public virtual ICollection<WebQuestionSection> WebQuestionSection1 { get; set; }
public virtual ICollection<WebQuestion> WebQuestion { get; set; }
The WebQuestion class is the same, it might contain another, nested, question and/or options for the question.
Now, I want to load this entire interview with as low of a loading time as possible.
I did this:
interview = _dbWeb.WebInterview.Include("WebQuestionPage")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionType")
... and so on
which, really, isn't that beautiful, easily read, or fast because it will join even if there is no nested children.
This is written is ASP.NET MVC using EF6.
I'm working with code that was started by someone other than me, so I'm not entirely familiar with the structure. However, I decided I needed to change one of the variables in one of the models. I needed to switch it from
[ForeignKey("JobFunctionDemandId")]
public virtual JobFunctionDemand JobFunctionDemand { get; set; }
public int JobFunctionDemandId { get; set; }
to
[ForeignKey("SpecificRequirementId")]
public virtual SpecificRequirement SpecificRequirement { get; set; }
public int SpecificRequirementId { get; set; }
I thought this would be a relatively small change. I realized, however, that these variables correspond to tables in a database, and so I switched all instances of JobFunctionDemand and JobFunctionDemandId to SpecificRequirement and SpecificRequirementId respectively. However, now when I try to run the Update-Database command in the package manager console, I get the error "The project 'ProjectName' failed to build."
I'm guessing the issue here is that I'm modifying the structure of an existing database, but I'm not really sure how to fix it.
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.
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.