Does including Collections in Entities violate what an entity is supposed to be? - asp.net-core

I am building a Web API using Dapper for .NET Core and trying to adhere to Clean Architecture principles. The API is consumed by an external Angular front-end.
I have repositories that use Dapper to retrieve data from the database, and this data then passes through a service to be mapped into a DTO for display to the user.
It is my understanding that an entity should be an exact representation of the database object, with no extra properties, and that I should use DTOs if I require some additional properties to show the user (or if I wish to obscure certain properties from the user too).
Suppose I have a DTO:
public class StudentDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Assignment> Assignments { get; set;}
}
and its corresponding Entity:
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
}
With this model, should I want to get a student with all of their assignments, I'd need to have two repository calls, and do something like this in the service:
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
var assignments = this.assignmentRepository.GetByStudentId(id);
return SomeMapperClass.Map(student, assignments);
}
But this seems inefficient and unnecessary. My question is, should I not just retrieve the Assignments when I get the student entity in the repository, using a JOIN? Or would this violate what an entity is supposed to be?
I apologise, I do realise this is a rather simple question, but I'd really like to know which method is the best approach, or if they both have their use cases

I think it would be more efficient, since map uses reflections, that is slower tens times
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
student.Assignments = this.assignmentRepository.GetByStudentId(id);
return student;
}
but the common way is
return _context.Students.Include(i=>i.Assignments).FirstOrDefault(i=> i.Id==id);
This is why the generic repository is a bad idea in the most casses, since it is hard to guess what set of data you will need.

Related

ASP.NET Core 3.1 Web API: how to protect sensitive data from return with model?

I have a Posts model class that has a relation with Users model.
When I call API to get Posts with the owner of it (user), it returns with all user info including password.
How to prevent model from returning sensitive information like passwords?
You should create new classes that you return from your actions. Only include the fields/information you want to return.
These classes are also known as Data Transfer Objects (DTO).
You can use [JsonIgnore] to avoid serializing the property value:
public class Users
{
public int Id { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public string Password{ get; set; }
//...
}

loosing dataAnottation when upload model from database

I have a big database existing database to comunicate with, and I'm using EF 5.0 database first, the problem I'm having is that if I create any data decoration like [stringlength(50)] on the class and then the databases is uploaded, when I "upload from database" all data annotations are gone. How can I do to keep them?
It's very simple: You Can't! Because those codes are auto-generated and will be over written on each model update or change.
However you can achieve what you need through extending models. Suppose that EF generated the following entity class for you:
namespace YourSolution
{
using System;
using System.Collections.Generic;
public partial class News
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int UserID { get; set; }
public virtual UserProfile User{ get; set; }
}
}
and you want do some work arounds to preserve your you data annotations and attributes. So, follow these steps:
First, add two classes some where (wherever you want, but it's better to be in Models) like the following:
namespace YourSolution
{
[MetadataType(typeof(NewsAttribs))]
public partial class News
{
// leave it empty.
}
public class NewsAttribs
{
// Your attribs will come here.
}
}
then add what properties and attributes you want to the second class - NewsAttribs here. :
public class NewsAttrib
{
[Display(Name = "News title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string Title { get; set; }
// and other properties you want...
}
Notes:
1) The namespace of the generated entity class and your classes must be the same - here YourSolution.
2) your first class must be partial and its name must be the same as EF generated class.
Go through this and your attribs never been lost again ...
The accepted answer may work for standard data operations, but I am trying to validate the model prior to the call to DbSet.Add using TryValidateObject. With the accepted answer, it is still not picking up on the data annotations.
What did work for me I found in a .NET Runtime GitHub thread, as proposed by what I'm inferring is one of the .NET developers.
Basically, this is a bug, and you have to force the model to recognize the metadata decorations using TypeDescriptor.AddProviderTransparent . . .
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(News), typeof(NewsAttrib)), typeof(News));
Once I make this call, TryValidateObject recognizes the data annotations and returns false when any of the constraints are not met.
Here's the link. I little more than half-way down, there's a working code sample in a .zip file.
https://github.com/dotnet/runtime/issues/46678

How to map two components of the same type with Fluent NHibernate AutoMap Conventions

We are using Fluent NH with convention based mapping. I have the following:
public class Foo() : Entity
{
public BarComponent PrimaryBar { get; set; }
public BarComponent SecondaryBar { get; set; }
}
public class BarComponent
{
public string Name { get; set; }
}
I have it to the point where it will create the foo table with a single name field. I've tried the following Override and it doesn't work.
public class FooOverride : IAutoMappingOverride<Foo>
{
public void Override(AutoMapping<Foo> mapping)
{
mapping.Component(x => x.PrimaryBar).ColumnPrefix("primary");
mapping.Component(x => x.SecondaryBar).ColumnPrefix("secondary");
}
}
Do I really need to do a full override mapping or can what I have here be made to work somehow?
I ran into this a couple of years ago when I started with FNH. It's one of the few scenarios I've seen where FNH Automapping does not "just work".
The approach that was suggested to me at the time, which I've used successfully (with entities however, not components) is to create empty, intermediate entities, and reference them in the descendant class.
In your case, you could create two new, empty classes that inherit from BarComponent (say, PrimaryBarComponent and SecondaryBarComponent).
Then, in your Foo class, declare them as:
public PrimaryBarComponent PrimaryBar { get; set; }
public SecondaryBarComponent SecondaryBar { get; set; }
This is a kluge, in my opinion, but it works fine with entities and lists of entities, and does not require any overrides or conventions.
I've never used components with FNH, so I don't know if a similar approach will work, but it might be worth investigating.
I ended up getting the way I have described in the question working. It turned out to be a problem with our AutoMappingConfiguration which inherits from DefaultAutomappingConfiguration. We weren't identifying Components properly.

NHibernate: Mapping different dynamic components based on a discriminator

My domain entities each have a set of "fixed" properties and a set of "dynamic" properties which can be added at runtime. I handle this by using NHibernate's dynamic-component functionality.
public class Product {
public virtual Guid Id { get; }
public virtual string Name { get; set;}
public virtual IDictionary DynamicComponents { get; }
}
Now I have the following situation
public class Customer {
public virtual Guid Id { get; }
public virtual string Type { get; set;}
public virtual IDictionary DynamicProperties { get; }
}
Where a CustomerType is something like "Online" or "InPerson". Furthermore an Online customer has dynamic properties "Name" and "IPAddress" and an InPerson Customer has dynamic properties "Name" and "Salesman".
Which customer types are available and the extra properties on them are configured in meta-data which is used to generate hbm files on application start.
I could figure out some way to knock this together using an intermediate DTO layer, but is there any support in NHibernate for this scenario? The only difficulty seems to be that all the different "types" of customer map to the same Customer class.
Maybe a stupid question, but why don't you just use two subclasses of Customer?
Other than that it is not immediately clear to me what it is you want NHibernate to support. Can you clarify what "any support in NHibernate for this scenario" means, what do you want NHibernate to do for you?
I think you can find a solution using the dynamic properties... in your subclasses, refer to the dynamic properties such as IPAddress { get { return DynamicProperties["ipAddress"] as IPAddress; } }
Interesting part for me is how do you map these properties in such a way that is scalable...
Did you come up with a different solution?

NHibernate add unmapped column in interceptor

I'm trying to save a mapped entity using NHibernate but my insert to the database fails because the underlying table has a column that does not allow nulls and IS NOT mapped in my domain object. The reason it isn't mapped is because the column in question supports a legacy application and has no relevance to my application - so I'd like to not pollute my entity with the legacy property.
I know I could use a private field inside my class - but this still feels nasty to me. I've read that I can use an NHibernate interceptor and override the OnSave() method to add in the new column right before my entity is saved. This is proving difficult since I can't work out how to add an instance of Nhibernate.type.IType to the types parameter of my interceptor's OnSave.
My Entity roughly looks like this:
public class Client
{
public virtual int Id { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual string Url { get; set; }
}
And my interceptor
public class ClientInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if (entity is Client)
{
/*
manually add the COM_HOLD column to the Client entity
*/
List<string> pn_list = propertyNames.ToList();
pn_list.Add("COM_HOLD");
propertyNames = pn_list.ToArray();
List<Object> _state = state.ToList();
_state.Add(false);
state = _state.ToArray();
//somehow add an IType to types param ??
}
return base.OnSave(entity, id, state, propertyNames, types);
}
}
Does anyone have any ideas on how to do this properly?
I can't say for sure since I've never actually done this (like Stefan, I also prefer to just add a private property), but can you just add a NHibernate.Type.BooleanType to the types array?
List<IType> typeList = types.ToList();
typeList.Add(new BooleanType());
types = typesList.ToArray();
EDIT
Yes, it looks like you are right; the types have an internal constructor. I did some digging and found TypeFactory:
Applications should use static
methods and constants on
NHibernate.NHibernateUtil if the
default IType is good enough. For example, the TypeFactory should only
be used when the String needs to have a length of 300 instead of 255. At this point
NHibernate.String does not get you thecorrect IType. Instead use TypeFactory.GetString(300) and keep a
local variable that holds a reference to the IType.
So it looks like what you want is NHibernateUtil:
Provides access to the full range of
NHibernate built-in types. IType
instances may be used to bind values
to query parameters. Also a factory
for new Blobs and Clobs.
typeList.Add(NHibernateUtil.Boolean);
Personally I wouldn't do it so complicated. I would add the private property and assign it a default value - finished. You could also consider a default value in the database, then you don't need to do anything else.
private virtual bool COM_HOLD
{
get { return false; }
set { /* make NH happy */ }
}
Before writing a interceptor for that I would consider to write a database trigger. Because with the Interceptor you are "polluting" your data access layer. It could make it unstable and you could have strange problems.