Ive been working with MVC for a while now and am used to creating a View Model class for every MVC view. Now I am trying out Web API and I think I may be hung up on this MVC mentality. My relationship looks like this:
public class Supplier
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<SupplierProduct> SupplierProducts { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<SupplierProduct> SupplierProducts { get; set; }
}
public class SupplierProduct
{
public int Id { get; set; }
public string Title { get; set; }
public int SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
I am working on the creation of the supplier where in the create form the user is able to select multiple products that already exist. In MVC, I would POST a view model that looks something like this:
public class SupplierCreateViewModel
{
public string Title { get; set; }
public ICollection<ProductViewModel> SelectedProducts { get; set; }
}
And in the controller I would first create the new Supplier then create a new SupplierProduct for each SelectedProduct. I implemented something like this in Web API in the POST action of my Supplier oData controller but it doesnt feel right. I think that instead, I need to change my approach and do something like this from the client:
Scrap the View Model design. (There arent really 'views' anymore anyway)
Have both a Supplier and a SupplierProduct Controller with a POST action on both.
On save, send my Supplier create request to POST api/Suppliers/.
Using the Id of the Supplier JSON in the response, send multiple create requests to POST api/SupplierProduct.
So my questions are:
Am I heading in the right direction with this approach?
Instead of View Models is there a different pattern I should use? DTO?
With the example given, am I forced to send 1 - n requests like that? This feels wrong.
Actually, it depends on your use-case. If your API is totally faced publicly, i would advice using DTO's. If it is for yourselve or for an internal team, i would stick with OData EF Models ( because it is quicker)
You can ( as usual) give the entire entity through your api.
You can use a viewmodel ( more like DTO's when using it in an API, but it's the same thing) and transform the methods accordingly. You can use automapper for that - it also transforms the $filter query, an example is found here : Web API Queryable - how to apply AutoMapper?.
Don't forget, an API has a lot of awesome advantages. OData uses Batch and Patch to change your entities. So i personally stick with Odata as entites most of the time, but that's a personal choice.
Related
I'm working with EF Core 1.1 on an asp.net mvc core project and stumbled upon, what I think, is non optimal SQL generation. But hey, I'm not an expert so I might be completly wrong :)
Models
public class Influencer
{
public int Id { get; set; }
public Instagram Instagram { get; set; }
public YouTube YouTube { get; set; }
// In the future, more social channels will be added so
// this is another concern I have how to architect/model properly
}
public class Instagram
{
public int Id { get; set;}
public string UserId { get; set; }
// More properties such as bio, image url, website and other stuff
}
public class YouTube
{
public int Id { get; set; }
public string ChannelId { get; set; }
// More properties such as bio, image url, statistics etc
}
The UserId and ChannelId are the "natural keys" (pardon my abuse of the correct SQL term :) and the Id properties "artifical keys" in a hope to create an interface/base class to simply various operations such as delete, refresh.
So to the question - how to correctly set this up in the modelBuilder to be correct?
The SQL I think should be the best is something like
Instagram
- Id (PK)
- UserId (AK)
- InfluencerId (FK)
YouTube
- Id (PK)
- ChannelId (AK)
- InfluencerId (FK)
Influencer
- Id
This should in effect constraint a one-to-one relationship between an influencer and its connected social platforms as well constraint that no influencer share the same social platform (just writing it make me unsure if I really want that since there might be cases where a couple for example share the same youtube-channel but are two individual influencers. Guess this is easier to achieve given the above design than introducing it later on)
On the the current modelling
modelBuilder
.Entity<InstagramChannel>()
.HasAlternateKey(i => i.UserId)
.HasName("AK_UserId");
modelBuilder
.Entity<Profile>()
.HasOne<Instagram>()
.WithOne()
.HasForeignKey(typeof(Instagram), "ProfileId")
.HasConstraintName("FK_Instagram_Profile")
.OnDelete(DeleteBehavior.Cascade);
This however generates a key to each of the social platforms on the Influencer such as Influencer.InstagramId, Influencer.YouTubeId causing two changes to database when a social platform is deleted.
Edit
So marking the properties Instagram, YouTube as not mapped produced the SQL that I was looking for. But now I'm uncertain whether or not I have missed something out - perhaps there is some optimizations enabled by EF core if the "parent table" has a direct relation to its related tables. Perhaps this just make it impossible for EF to evaluate the Include(i => i.Instagram) statement since it probably want to do so without the need for a join.
So quite a long post and perhaps not really a question (well, I said it :) and probably not suitable in this forum. But hey, there are many great guys and girls out there that might wan't to share their knowledge!
Thanks
You can achieve your goal even without using modelBuilder, using attributes:
public class Influencer
{
public int Id { get; set; }
[InverseProperty("Influencer")]
public Instagram Instagram { get; set; }
[InverseProperty("Influencer")]
public YouTube YouTube { get; set; }
}
public class Instagram
{
public int Id { get; set;}
public string UserId { get; set; }
public int InfluencerId { get; set; }
[ForeignKey("InfluencerId")]
public virtual Influencer Influencer { get; set; }
}
public class YouTube
{
public int Id { get; set; }
public string ChannelId { get; set; }
public int InfluencerId { get; set; }
[ForeignKey("InfluencerId")]
public virtual Influencer Influencer { get; set; }
}
You may remove InfluencerId properties if you don't like them (I prefer to have explicit Id field to be able to set int-value there instead of assigning Influencer instances and reading them from DB only for this).
Also, you may remove virtual keywords, because EF Core (currently) do not require them (but it also does not support LazyLoading yet, so I prefer to keep "virtual" in case it will be required later)
I am learning ASP.NET MVC. And In order to create PK and FK I have added some code in models in .cs file as below
public class Courses
{
public int CourseID { get; set; }
[Key]
public string CourseName { get; set; }
public string Description { get; set; }
public int Duration { get; set; }
public virtual ICollection<Instructors> Instructors { get; set; }
}
public class Instructors
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CourseName { get; set; }
[ForeignKey("CourseName")]
public virtual Courses Courses { get; set; }
}
My question is then what is the significance of Fluent API. I have successfully created PK and FK using above code in model. Then what is Fluent API and why it is needed?
Entity framework Fluent Api is an alternative way to define database schema using Entity framework Code First approach. The syntax that you used in your question uses data annotations which is other common approach.
The major advantage of FluentApi is that you don't have to have actual access to your model classes in order to decorate them. For example when you have your models in separate assembly decorating them with annotations is simply impossible. On the other hand by using EF FluentAPI you can easily do it with something like:
modelBuilder.Entity<Courses>().HasKey(c => c.CourseID);
At the bottom line both approaches generate exactly the same database schema.
So if you prefer to use data annotations and your project structure allows it, you can use it without any doubts. In those cases when you can not use the data annotations approach and you still want to use Code First approach, FluentApi is the right way to look at.
We are using Entity Framework + Repository Pattern in a web based application to fetch database . Because of our complex business, our models are getting complex sometimes and this cause strange behaviour at Entity Framework eager loading system.
Please imagine our real model like this. We have tables, boxes which are on table, pencil cases which can be on table or in the box and pencils that can be on the table or in the box or in the pencil case.
We had modelled this in our application like this.
public class Table
{
public int TableID{ get; set; }
public virtual ICollection<Box> Boxes{ get; set; }
public virtual ICollection<PencilCases> PencilCases{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class Box
{
public int BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
public virtual ICollection<PencilCases> PencilCases{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class PencilCases
{
public int PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
[ForeignKey("BoxID")]
public virtual Box Box{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class Pencils
{
public int PencilID{ get; set; }
public int? PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
[ForeignKey("BoxID")]
public virtual Box Box{ get; set; }
[ForeignKey("PencilCaseID")]
public virtual PencilCase PancelCase{ get; set; }
}
Our repository pattern implementation similar with this tutorial, http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
So we call get method like this.
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
So the problem is the result is very different from my expectations;i expect only Boxes,PencilCases and Boxes.Pencils collections will be fetched, but all the Pencil entities fetched from database including Pencils, PencilCases.Pencils and Boxes.PencilCases.Pencils. This recursive fetch causes OutOfMemoryException because amount of data.
I couldn't understand why Entity Framework fetches all Pencils except Boxes.Pencils. I also tried to specify including list with Expression instead of Query Path but result didn't change.
first off - I'm fairly new to EF myself so please excuse if the following is not 100% accurate. However, I've dealt with this exact same problem just a couple of days ago, so hopefully this will help.
The problem is that when EF loads a specific entity, it will add that entity to every part of the Data Model that it appears in - not just the parts that were explicitly loaded.
This means that every Pencil in Boxes.Pencils that is also in the ICollection of Table.Pencils will be automatically resolved even though you did not specifically ask for it.
By itself that fact does not present a problem, and can even be helpful in a user-driven MVC application.
Where it all goes wrong is when you try to do anything that recurses trough the Data Entity, such as trying to map the self-recursing Data Entity to a Business Model or trying to turn the self-recursing data entity into JSON/XML.
Now, there are several solutions to this problem:
Implement a mapper / encoder that hashes / remembers each object and only adds it once:
The problem with this one is that it can lead to some hard-to-predict results, especially when you want / need the object in multiple places. Additionally, hashing and comparing every object could be costly.
Implement a mapper / encoder that can be configured to ignore some properties
Relatively simple - if you can specify that you don't want to map or encode Pencil at all, you won't have any issues. Downsides are of course that you could still encounter a stackoverflow if you are not vigilant about specifying the ignored properties.
Implement a mapper / encoder with specifyable recursion depth
This is a very simple and pretty decent solution - simply set a hard limit on recursion depth, either on a global or on a per-type basis, and you won't have any more stackoverflows. Downside is that you would still end up with elements that you don't want, and thus get a unnecessarily bloated return object.
Implement custom business entities
This is probably the best solution - simply create a new business entity with the offending navigational properties removed. The primary downside is that it would require you to create different business entities for different purposes.
Here is a example:
// Removed Pencils
public class BusinessTable
{
public int TableID{ get; set; }
public IEnumerable<Box> Boxes{ get; set; }
public IEnumerable<PencilCases> PencilCases{ get; set; }
}
// Removed Table & PencilCases
public class BusinessBox
{
public int BoxID{ get; set; }
public int TableID{ get; set; }
public IEnumerable<Pencils> Pencils{ get; set; }
}
// Removed Table & Box & Pencils
public class BusinessPencilCases
{
public int PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
}
// Removed Table, Box, PencilCase
public class BusinessPencils
{
public int PencilID{ get; set; }
public int? PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
}
Now when you map your Data Entity to this set of Business Entities, you won't get any more errors.
For the mapping aspect of this, theres 2 solutions: Manually doing things / using a mapping factory Example of Model Factory, ValueInjecter and AutoMapper - the latter two being available NuGet packages.
For AutoMapper:
I don't use AutoMapper, but you'd have to create a config file that looks something like this:
Mapper.CreateMap<Table, BusinessTable>();
Mapper.CreateMap<Box, BusinessBox>();
Mapper.CreateMap<PencilCases, BusinessPencilCases>();
Mapper.CreateMap<Pencils, BusinessPencils>();
And then in your query:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = Mapper.Map<IEnumerable<Table>, IEnumerable<BusinessTable>>(tables);
Or
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils").Project().To<IEnumerable<BusinessTable>;
For more info pertaining AutoMapper ( like how to set up a config file ): https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
For ValueInjecter:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = new List<BusinessTable>().InjectFrom(tables);
Or:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = tables.Select(x => new BusinessTable.InjectFrom(x).Cast<BusinessTable>());
It might also be worthwhile to look at additional ValueInjecter Injections, like SmartConventionInjection, Deep Cloning, Useful Injections and a ORM with ValueInjecter guide.
I also made a few injections for my own project that may be of use to you, which you can find On my Github
With MaxDepthCloneInjector for example, you can supply a dictionary of (property names, max recursion depth) and it will only map values included in the dictionary, and only until the specified level.
Two more pieces of advice:
If you want a bit more freedom with your queries, you should consider using the Query Expression Syntax for some of your more complex needs. Theres also some good information in this answer on SO: How to limit number of related data with Include
If you are planning to run queries including navigational properties like the one in your example: STICK WITH EAGER LOADING. A query like that in Lazy Loading would lead to the N + 1 problem. As a rule of thumb:
Use Lazy Loading if you don't need the entire result set right away, for example if you are developing a application where data requirements naturally expand based on the User's interaction with the application.
Use Eager Loading if you need the entire result-set right away, for example in a Web Api, or a application that needs to work with the complete entity.
Best of luck,
Felix
I have two view models:
public class MasterPageViewModel
{
public string Meta { get; set; }
}
public class Entry : MasterPageViewModel
{
public int EntryID { get; set; }
public string Title { get; set; }
public DateTime PubDate { get; set; }
}
Index page returns a list of entries, so in the view contains:
...Inherits="System.Web.Mvc.ViewPage<IEnumerable<bl.Models.Entry>>"
Then the master page contains
...Inherits="System.Web.Mvc.ViewMasterPage<bl.Models.MasterPageViewModel>"
And here is the error that I am getting:
The model item passed into the dictionary is of type 'System.Linq.EnumerableQuery`1[bl.Models.Entry]', but this dictionary requires a model item of type 'bl.Models.MasterPageViewModel'.
I can easily bypass that error by using ViewData dictionary on the master page, but in my case I would prefer strongly typed approach. In future I want to be able to add lists of categories and tags that would appear on the master page.
I have something like the structure you describe in an MVC site I'm working on now. I haven't found a really satisfying answer -- in many cases it feels like you want two distinct models, one for the master page and one for content pages. Unfortunately, that's not how MVC works.
I've only come across one solution, other than the ViewData option you mentioned.
Base Class
Build a base class that all your models inherit off.
The base class contains all the properties you need in your master page.
Good: it's simple inheritance
Bad: you end up with wrapper methods / models for basic things like passing an enumberable collection.
So in your case you would end up with something like ...
public class MasterPageViewModel {
public string Meta { get; set; }
}
public class Entry : MasterPageViewModel {
public IEnumerable<bl.Models.EntryItem> Items {get; set }
}
public class EntryItem{
public int EntryID { get; set; }
public string Title { get; set; }
public DateTime PubDate { get; set; }
}
And your Index page would look like...
...Inherits="System.Web.Mvc.ViewPage<bl.Models.Entry>"
It's kind of a pain in the butt because you end up with lots of little models. Once I got used to it, however, i've stopped thinking about it.
HTH,
-eric
I have (for example) these two objects:
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
...
public List<Order> Orders;
}
class Order
{
public int Id { get; set; }
public string Description { get; set; }
...
}
I want one View on which you can edit the Customer data
(that part is already working fine), but I want some kind of
'dynamic grid' to add/remove/update orders on the same page.
How can you do this? With jQuery you can add or remove html controls, but to add or remove
an Order object with it?
Any ideas?
thanks,
Filip
I would recommend you checking out the following blog post from Steve Sanderson which illustrates a nice technique which could be used for editing a variable length list in ASP.NET MVC 2.