I have two related entities called DataTag and TagSource that look like the following:
public class DataTag : BaseModel
{
[Column("DataTagId")]
public override Guid ID { get; set; }
public string Tag { get; set; }
public Guid TagSourceId { get; set; }
public TagSource TagSource { get; set; }
}
public class TagSource : BaseModel
{
[Column("TagSourceId")]
public override Guid ID { get; set; }
public string Description { get; set; }
public bool IsInternal { get; set; }
public string Source { get; set; }
public ICollection<DataTag> DataTags { get; set; }
}
I am allowing the user to Include the navigation properties through the url like "/api/DataTags?Include=TagSource". The problem is when I include the TagSource, it also includes the collection of DataTags in that object which I don't want unless the user specifies it (For example "/api/DataTags?Include=TagSource.DataTags". Is there any way to stop that property from being loaded when I include the TagSource? I have tried making the properties virtual and turning lazy loading off globally but that didn't work. The reason I haven't marked them virtual is because I am using AutoMapper and I only want to include the navigation properties that the user specifies.
As in the comments you need to create a DTO object. There is a good article here detailing how to do this with WebAPI
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-5
Edit.
The problem with this is you will need a lot of different DTO objects for each possible outcome which could become messy. If your return type is JSON you can add this attribute to your properties:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
Firstly : Apologies for my English.
Secondly : I had the same issue with a code first database model that creates foreign keys this way : public virtual Collection<Object> Objects {get; set;}
and I found a workaround by setting the property setter as private:
public virtual Collection<Object> Objects {get; private set;}
Then the EF cannot populate the Objects collection because with a private set you can only assign a value in constructors.
Related
I have a Question.
I have 2 Dto Object and one Model.
What i'm trying todo is map the AssetDTO to the Asset Model using Automapper.
I have no clue on how to accomplish this.
public class AssetDTO
{
public string Code { get; set; }
public string CodeType { get; set; }
public List<MetricDataDTO> Data { get; set; }
}
public class MetricDataDTO
{
public string Value{ get; set; }
public string Flow { get; set; }
}
I have one model that look like this.
public class Asset
{
public string Code { get; set; }
public string CodeType { get; set; }
public string Value{ get; set; }
public string Flow { get; set; }
}
I tryed setting up the mapping with automapper but without any luck :(
Hope anyone can help me out,Thanks in advance!!
if suppose the value and flow inside asset is array, I think your current model for Asset need to be change (i.e. by creating new field List<MetricData> like List<MetricDataDTO>). Later you can check this one AutoMapper - Mapping list of objects to bigger list of objects.
In case for each asset there's only one value and flow, your model for AssetDTO will become:
public class AssetDTO
{
public string Code { get; set; }
public string CodeType { get; set; }
public MetricDataDTO Data { get; set; }
}
And for such case, here the example for nested object (based on Using automapper for nested objects): https://dotnetfiddle.net/qVg59r
Below answer also much more simpler https://stackoverflow.com/a/74471904/10766263
Thanks to Lucian Bargaoanu!
If you need more control when flattening, you can use IncludeMembers. You can map members of a child object to the destination object when you already have a map from the child type to the destination type.
This allows you to reuse the configuration in the existing map for the child types MetricDataDTO when mapping the parent types AssetDTO and Asset .
It works in a similar way to mapping inheritance, but it uses composition, not inheritance.
cfg.CreateMap<AssetDTO, Asset>().IncludeMembers(s=>s.MetricDataDTO);
You can check this link for details.
https://docs.automapper.org/en/latest/Flattening.html#includemembers
Hope it can help you.
I have not worked with EF for a while.
As an exercise, I am writing a core web api that allows keeping track of a user medias.
A media can be a postcard, a photo album, a recording, a book...
I would like to know what is the way to go/best practice in writing the Add (createMedia) method:
[HttpPost]
public async Task<ActionResult<bool>> Add(Media media)
My model is comprised of several specific classes representing one type of media - like Postcard, Photoalbum, recording, etc. In addition, there is a Media type - which contains shared properties among all media types:
public class Media
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public MediaType Type { get; set; }
public bool InUse { get; set; }
public string Date { get; set; } //yyyy-mm-dd
public string Owner { get; set; }
}
As an example of one of the specific media types:
public class Postcard
{
[ForeignKey("Id")]
public int MediaId { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
I designed my EntityFramework db to consist of a 1-1 relation between Media and the relevant specific media table.
What is the best practice in writing the Add method ? Should it receive a Media object, and based on MediaType create the respective type ? I started with this approach, and had the action receive a second parameter named detailsJson, which I would parse and fill the respective object using reflection, but figured out that POST binding will not bind 2 objects.
I'm not well versed in design patterns.
Should there exist as many AddBook, AddPostcard... as many media types ?
I understand all models should be POCO objects, without inheritance.
I read about DTOs, but does not see how it helps me here.
I guess the Postcard and Book although both media types, they have different properties? for example, From, To, Place fields will not be there in book, whereas Book might have ISBN, Author, Genre etc.
so essentially they are different POCO objects altogether, media type is just a relationship they have in common
In this case, it's OK to have separate Add methods like AddBook, AddPostcard etc.
Update:
The individual POCO objects can refer to Media type to avoid duplicating same properties, same as underlying entity relationship
public class Postcard
{
public Media Media { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
What about having a Key-Value table?
public class MediaValue {
public string MediaId {get;set;}
public string AttributeName {get;set;}
public string AttributeValue{get;set;}
//the key is the couple MediaId - AttributeName
}
public class Media {
//shared properties
public MediaType MediaType {get;set;}
public IEnumerable<MediaValue> Attributes {get;set;}
}
public enum MediaType {
PostCard, PhotoAlbum, ....
}
then you could add the DTOs foreach mediatype
public abstract class MediaDTO {
//shared properties
}
public class PostCardDTO : MediaDTO {
public string PostCardSpecificAttribute {get;set;} // this.PostCardCustomAttribute = aMediaDbObject.Attributes.FirstOrDefault(x => x.AttributeName == "PostCardSpecificAttribute");
}
I'm following along the ASP.NET MVC 5 book, but I've ran into an itch that the book doesn't seem to scratch. I have an Album model as so:
namespace MvcMusicStore.Models
{
public class Album
{
public virtual int AlbumId { get; set; }
public virtual int GenreId { get; set; }
public virtual int ArtistId { get; set; }
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
public virtual string AlbumArtUrl { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
}
}
To make a long story short, the Genre and Artist models both have a field called Name. When I list these using the StoreManagerController, it displays simply as "Name" in each of the headers. I can add a DataAnnotation to Genre and Artist suchs as [Display(Name="Artist Name")], but I only want it to display as "Artist Name" in this particular instance. I don't want it to be so specific when I am on the "Edit Artist Page."
I understand that I should go about doing this by using a ViewModel, but I am still confused as the view model would still just be pulling in the object, and that object's Display annotations are set in the model itself.
Or better yet, is this something that's best left to the markup?
Not if you use view models properly. Many people end up creating view models like:
public class FooViewModel
{
public Foo MyFoo { get; set; }
}
That's just a waste of time. Instead, you view models should completely stand in for whatever entity your editing, which means, instead of just referencing the entity, you create properties in your view model for all the properties in your entity that you want to view/edit. Then, in your controller actions, you "map" to and from your entity and view model, which is to say, you just set the properties on one with the values of the appropriate properties on the other.
In your situation then, you would need something like:
public class AlbumViewModel
{
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public GenreViewModel Genre { get; set; }
public ArtistViewModel Artist { get; set; }
}
public class ArtistViewModel
{
public string Name { get; set; }
...
}
public class GenreViewModel
{
...
}
Then, you can set the display name to be whatever you want on this view model. If you need a different display name in another context, create a separate view model for that.
Also, what's up with all the virtuals? The virtual keyword merely means that the property/method can be overridden by a subclass. While it technically doesn't hurt anything to just make everything virtual, it's code smell unless you truly intend something to be overridden, or even subclassed in the first place. Traditionally, on entities, the only thing you'll ever add virtual to is navigation properties, as this allows Entity Framework to apply its lazy loading logic to your entity. (It literally creates subclasses of your entities dynamically, called "proxies", that add the lazy loading logic to the navigation properties' getter.) If you don't have a navigation property or even if you just don't want lazy loading enabled for that navigation property, then you shouldn't use virtual, unless you really mean to.
I would probably do something like this.
public class AlbumViewModel
{
public int AlbumId { get; set; }
public AlbumGenre Genre { get; set; }
public AlbumArtist Artist { get; set; }
}
[MetadataType(typeof(AlbumArtistMetadata))]
public class AlbumArtist : Artist {
private class AlbumArtistMetadata {
[Display(Name="Artist Name")]
public string Name { get; set; }
}
}
[MetadataType(typeof(AlbumGenreMetadata))]
public class AlbumGenre : Genre
{
private class AlbumGenreMetadata
{
[Display(Name = "Genre Name")]
public string Name { get; set; }
}
}
Though I'm not sure I'd inherit from the entities, but instead create models based on the entities.
I'm building a fairly simple MVC project and still getting my head around where to use navigation properties and foreign keys with code first.
This is the main model class:
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
public GroceryCategory Category { get; set; }
public QualityProfile Quality { get; set; }
public GroceryStore BestStore { get; set; }
public double BestPrice { get; set; }
public double LastSeenPrice { get; set; }
//Navigation Properties
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}
and these are the relating classes:
public class GroceryStore
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Uri Website { get; set; }
}
public class QualityProfile
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
/// <summary>
/// Rank out of 1-10, 10 being the best
/// </summary>
public byte Ranking { get; set; }
}
public class GroceryCategory
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Which brings me to my question, is the navigation property of SimilarItems I have in the GroceryItem class sufficient to represent a list of multiple grocery items or does this not work as it is referring to itself?
Additionally...do the Category, Quality and BestStore properties require ID properties to represent a foreign key inside of the GroceryItem class (e.g. CategoryID), or is the way I have this represented OK?
----EDIT----
--Refactored Code--
I've re-factored my model based on the suggestions below, which I think better accommodates the suggestions you've made (yes a 2nd time), realised my model was a little flawed and extracted out the price component into a separate purchases Model.
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
[ForeignKey("Quality")]
public int QualityID { get; set; }
//Navigation Properties
public virtual QualityProfile Quality { get; set; }
public virtual GroceryCategory Category { get; set; }
}
However the last thing I'm uncertain about which is on topic to this post, is if I have a collection as a part of the model (one that does not reference itself like in the first example), can I just represent that with a navigation property or does an extra step need to be taken?
Ie. If I was to allow multiple different categories on a GroceryItem, instead of looking like this:
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }
it would look like this:
public virtual ICollection<GroceryCategory> Categories { get; set; }
The best answer to your question(s) is, "It depends". Navigation properties are one way of informing Entity Framework that there's a relationship between entities. By convention, if you have a navigation property such as:
public Category Category { get; set; }
Entity Framework will create a column on the table named in the form of [RelatedPropertyName]_[RelatedPK]. Given your classes, the property above would cause a column named Category_ID. There's nothing more you need to do make it work. The relationship will automatically be handled by EF.
However, doing it this way, you won't have access to this foreign key property. It's not exposed in the public API of your entity. Often, especially when selecting related items from a select list and similar such scenarios, this becomes problematic, as you must store the selected value some place else, usually a property on a view model, and then use this to query the related thing from the database before setting it on the entity it belongs to and finally saving the entity. Whereas, with an actual foreign key property, you can simply post directly back to this and Entity Framework will automatically wire up the related entity. As a result, I tend to always follow the following pattern with my navigation properties:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
In most scenarios, Entity Framework will automatically connect those two, such that FooId will hold the foreign key relationship for the Foo navigation property. However, occasionally, EF will trip up and try to create the implicit foreign key behind the scenes, still, but you can correct that behavior by explicitly telling EF that this is the foreign key:
[ForeignKey("Foo")]
public int FooId { get; set; }
Roughly the same applies with collection navigation properties. EF will see this as an indication that there's a one-to-many relationship in play and add the implicit foreign key on the opposite entity. Given your collection:
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
The opposite entity is actually the same entity, which presents an interesting use case. Typically, EF would handle this by assuming there's a one-to-many relationship. You'd end up with a column named GroceryItem_ID on your dbo.GroceryItems table. Here, though, you would not only have no access to the foreign key directly, but you also have no public API for accessing the parent GroceryItem either. That may not be a problem, but it's something to be aware of. The only way you'd be able to manage the relationship is through the collection on the parent, not through a child item in that collection.
However, since this is self-referential and you have not specify a foreign key or instance navigation property, all EF will see is a collection on both sides of the relationship, so my guess is that you'll actually end up with an M2M with an intermediary table. I can't test that theory out myself at the moment, and I haven't tried this particular scenario myself previously.
To create a true one-to-many, you would need to create another navigation property similar to:
public virtual GroceryItem ParentGroceryItem { get; set; }
And, even, then, I don't think EF will get the point without a little Fluent configuration:
HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);
You could also use WithRequired in other scenarios instead of WithOptional, which would obviously make the relationship a required one, but since this is self-referential, it's impossible to have it required, because there will have to be at least one root node with no parent.
I am using Automapper for mapping my domain model and DTO.
When I map Mapper.Map<SiteDTO, SiteEntity> it works fine.
But when I use collections of the same entities, it doesn't map.
Mapper.Map<Collection<SiteEntity>, Collection<SiteDTO>>(siteEntityCollection);
AS per Automapper Wiki, it says the lists implementing ICollection would be mapped, I am using Collection that implements ICollection, but automapper doesn't map it. Am I doing something wrong.
public class SiteEntity //SiteDTO has exactly the same properties, so I am not posting it here.
{
public int SiteID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public byte Status { get; set; }
public int ModifiedBy { get; set; }
public DateTime ModifiedDate{ get; set; }
public long TimeStamp{ get; set; }
public string Description{ get; set; }
public string Notes{ get; set; }
public ObservableCollection<AreaEntity> Areas{ get; set; }
public void SiteEntity()
{
Areas=new ObservableCollection<AreaEntity>();
}
}
EDIT: SiteEntity updated to include the constructor.
I have been using IList<> without any problems.
I would check the mapping of child domain models first.
Most probably they are not set yet. In your example: mapping of AreaEntity -> AreaEntityDto.
Mapper.Map<AreaEntity, AreaEntityDto>
Code example from wiki:
Mapper.CreateMap<ParentSource, ParentDestination>()
.Include<ChildSource, ChildDestination>();
Mapper.CreateMap<ChildSource, ChildDestination>();
Based on the code you posted Automapper will fail to map because you do not have a default constructor for SiteEntity that creates a new ObservableCollection Areas.
Since this is not there you will get a null reference exception when it trys to map Areas.