EF Core how to create relational model - c#

I'm a beginner on Asp.Net Core and I need to do de following:
I have the models curso and unidade, they share a many to many relationship.
Both models work fine by themselves, my problem however is that I can't make the relational model.
The idea is to make an endpoint that receives an object with one unidade and an array of curso and the API would add or remove the relationships accordingly.
The following code is what I have made so far, I'm getting an error Identity_Insert.
Am I on the right direction with this? Or is there another proper better way of doing this?
Modelo de curso
public class Curso
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long curId { get; set; }
[Required]
[StringLength(80)]
public string curDescricao { get; set; }
[Required]
[StringLength(1)]
public string curStatus { get; set; }
[StringLength(20)]
public string curCodExterno { get; set; }
[StringLength(60)]
public string curObservacao { get; set; }
}
Modelo de unidade
public class Unidade
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long uniId { get; set; }
[Required]
[StringLength(80)]
public string uniDescricao { get; set; }
[Required]
[StringLength(1)]
public string uniStatus { get; set; }
[StringLength(20)]
public string uniCodExterno { get; set; }
public byte[] uniImagem { get; set; }
}
Modelo de CursoUnidade
public class CursoUnidade
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long cuuId { get; set; }
/*[Required]
public long cuuCurId { get; set; }
[ForeignKey("cuuCurId")]*/
public List<Curso> Curso { get; set; }
/*[Required]
public long cuuUniId { get; set; }
[ForeignKey("cuuUniId")] */
public Unidade Unidade { get; set; }
}
Serviço de unidade
public void AddTeste(CursoUnidade cursoUnidade)
{
_contexto.Add(cursoUnidade);
_contexto.SaveChanges();
}

The problem is that the Curso and Unidade instances on the CursoUnidade that's being passed in are not being tracked by EF. As a result, when you attempt to add it and save, it's attempting to create those two entities again. However, since they both already have ids, it cannot do that and fails.
This is one of the many problems associated with trying to save what's passed into your action directly. You should use a view model and then map that over the the actual entity you end up saving. This forces you to be explicit about what you're doing and results in less bugs such as this one. Also, unless you're actually intending the two related instances to be able to modified at the same time they're being associated, it doesn't make sense to post the entire entity, anyways. This can easily lead to an overpost exploit, where the user makes modifications to entities they shouldn't be able to make, and because of the sloppy handling of the post server-side, those just get blindly saved to the database.
Instead, use a view model/DTO like:
public class CursoUnidadeDTO
{
public long CursoId { get; set; }
public long UnidadeId { get; set; }
}
Then, in your action:
public void AddTeste(CursoUnidadeDTO cursoUnidade)
{
var curso = await _contexto.Cursos.FindAsync(cursoUnidade.CursoId);
var unidade = await _contexto.Unidades.FindAsync(cursoUnidade.UnidadeId);
if (curso == null || unidade == null)
{
return BadRequest("Cannot create relationship");
}
_contexto.Add(new CursoUnidade { Curso = curso, Unidade = unidade });
await _contexto.SaveChangesAsync();
}
Or, if your CursoUnidade class had explicit foreign key properties, you wouldn't even need to look anything up:
_contexto.Add(new CursoUnidade { CursoId = cursoUnidade.CursoId, UnidadeId = cursoUnidade.UnidadeId });
Alternatively, you can simply attach these entities first, so that EF knows about them:
_contexto.Attach(cursoUnidade.Curso);
_contexto.Attach(cursoUnidade.Unidade);
_contexto.Add(cursoUnidade);
await _contexto.SaveChangesAsync();
That will solve your immediate issue, but like I said previously, it will also allow overposts, and if that's not something you're explicitly allowing, could be very dangerous.

Related

EF Core 2.0 - Navigation properties can only participate in a single relationship

Let's say I have these models:
public class Component
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public List<ComponentUpdate> Updates { get; set; }
public ComponentUpdate LastUpdate { get; set; }
}
public class ComponentUpdate
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public Component Component { get; set; }
public string Message { get; set; }
}
The reason I'm saving the LastUpdate field instead of manually pulling it according to the highest 'TimeStamp' is because of speed. It would be faster to store a reference instead of checking the entire list every request.
When I'm trying to migrate the DB it throws an error saying I cannot have my properties participate in more than a single relationship.
I'm mapping the relationships in my context class and I don't think I'm doing it right since I have ComponentUpdate.Component mapped twice.
I've looked on several solutions but some were outdated and some just did not fit this scenario.
Thanks for helping.
Edit
Mapping accordingly:
modelBuilder.Entity<Component>().HasMany(c => c.Updates).WithOne(u => u.Component);
modelBuilder.Entity<ComponentUpdate>().HasOne(u => u.Component).WithOne(c => c.LastUpdate);

How to model with EntityFramework extensible fields for the entities

I have the following requirement, on my app the Entities will come with some fields, however the user needs to be able to add additional fields to the entity and then values for those fields.
I was thinking something like this but I am not sure if it would be a good approach or not.
The base class is an entity (Not sure which fields I need to add here)
public class Entidad
{
}
Then the Company Class will inherit from Entity
public class Empresa : Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
As you can see there is an ICollection of additional fields. that class would have the fieldname, type and id
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public Tiposcampo TipoCampo { get; set; }
}
and then the field value would be something like this:
public class ValorCampo
{
public Entidad Entidad { get; set; }
public CampoAdicional Campo { get; set; }
public string ValorTexto { get;set ; }
public int ValorNumerico { get; set; }
}
However I am not sure if this is the correct model classes for my scenario and whether it would create the tables correctly.
EF works with lazy load so at least there are several "virtual" missings.
In all properties that does not use primitive types and in collections.
Can you extend more than one entity with additional fields? If so you need that ValorCampo contains the entity (Entidad) but the entity should have the Id so you need to move the Id from Empresa to Entidad. Otherwise you need ValorCampo should refer to Empresa not to Entidad

How to add userId to every query without adding it to all domain classes?

I'm working with sqlite and the c# sqlite-net library.
Some of my entities:
public class Product
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Brand { get; set; }
[MaxLength(50)]
public string Type { get; set; }
[MaxLength(50)]
}
public class ProductImage
{
public int Id { get; set; }
public string Name { get; set; }
public string Container { get; set; }
public int ProductId { get; set; }
}
Now all entities belong to a user and the user should always work only with his own entities. So when I insert an entity in the database I want to store the userId with it.
However... I don't want to add the userId to all my domain classes.
I'm storing my entities like this right now:
await _databaseManager.GetDatabaseInstance().InsertAsync(entity);
and selecting like this:
var products = await _databaseManager.GetDatabaseInstance().Table().ToListAsync();
So is there a way with sqlite-net to add the userId to the database without adding it to all domain classes?
Doesn't it support inheritance, as in looking at the type's hierarchy? I would suggest it would, or at least should. So, if it does you could use an abstract base class or an interface. Something like this:
public abstract class StandardEntity {
public int UserId { get; set; } // decorate with attributes as necessary
}
And inherit:
public class Product : StandardEntity {
}

Nested ViewModel Classes in asp.net MVC

I have four MVC model layer domain classes.
namespace MvcMobile.Models.BusinessObject
{
public class Speaker
{
public int SpeakerID { get; set; }
public string SpeakerName { get; set; }
}
public class Tag
{
public int TagID { get; set; }
public string TagName { get; set; }
}
public class Seminar
{
public string Seminar_Code { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Room { get; set; }
}
public class Seminar_Detail
{
public string Seminar_Code { get; set; }
public int SpeakerID { get; set; }
public int TagID { get; set; }
public string DateAndTime { get; set; }
}
}
I would like to make CRUD operation by using these classes. So I create two VeiwModel Classes.
namespace MvcMobile.ViewModel
{
public class Seminar_Root_ViewModel
{
public Seminar_Subsidiary_ViewModel Seminars { get; set; }
public List<Speaker> Speakers { get; set; }
public List<Tag> Tags { get; set; }
}
public class Seminar_Subsidiary_ViewModel
{
public Seminar Seminar { get; set; }
public List<Seminar_Detail> Seminar_Detail { get; set; }
}
}
For Controller layer, I consider that I will use Seminar_Root_ViewModel to make the whole CRUD operation processes.
What I would like to ask is that Is this proper way or correct way?
If you have more elegant way to make model layer and ViewModel layer, Please let me get suggestion.
Every suggestion will be appreciated.
[updated]
Let's assume that I make master-Detail form design.
Speaker and Tag are just look-up tables for dropdownlist or some controls like that.
Seminar is Master Data and Seminar_Detail will be Item Grid Data.
So As for this scenario, all of this classes are needed for this program.
Please let me know if my thinking is wrong.
The only thing I can see is if you are not going to re-use your Seminar_Subsidiary_ViewModel view model you could skip it.
If you are going to need those two properties Seminar and Seminar_Detail on another view or ajax call, it's perfectly fine to have that kind of separation.
Personally I'm not a huge fan of _ on class name, but that have nothing to do with the question.

Many to Many self Join with Entity Framework Code First

Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.

Categories

Resources