Create sub-type instances in ASP.NET Core 2.2 - asp.net-core-2.2

I am developing an ASP.NET Core application. There are several entities, some of which have one-to-many relations with each other i.e.
Project 1->* Priority, Project 1->* Milestone; Milestone 1->* Issue...
All navigation properties of all entities are virtual and I have already configured lazy loading in OnConfiguring method in the DbContext class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
}
The situation is following: I have already created an instance or Project type and now I want to create instances of other sub-types which should be related to the already created Project instance.
My logic for creating of Priority instance is to pass a projectId to the priority entity which will be added to the corresponding DbSet.
The entity models:
public class Project
{
[Required]
public virtual ICollection<Priority> Priorities { get; set; }
}
public class Priority
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ProjectId { get; set; }
[Required]
public virtual Project Project { get; set; }
}
The priorityInputModel from the form:
public class PriorityInputModel : IMapTo<PriorityServiceModel>
{
[Required]
public string Name { get; set; }
[Required]
public string ProjectId { get; set; }
}
The PriorityServiceModel:
public class PriorityServiceModel : IMapFrom<Priority>, IMapTo<Priority>
{
public int Id { get; set; }
public string Name { get; set; }
public string ProjectId { get; set; }
public ProjectServiceModel Project { get; set; }
}
The controller:
[HttpPost]
public async Task<ActionResult> Create(PriorityInputModel inputModel)
{
var priorityServiceModel = inputModel.To<PriorityServiceModel>();
var priorityServiceModelResult = await this.priorityService.CreateAsync(priorityServiceModel);
return this.RedirectToRoute(
routeName: ValuesConstants.DefaultRouteName,
routeValues: new
{
controller = ValuesConstants.ProjectControllerName,
action = ValuesConstants.DetailsActionName,
id = priorityServiceModelResult.ProjectId,
});
}
The service method:
public async Task<PriorityServiceModel> CreateAsync(PriorityServiceModel priorityServiceModel)
{
var priority = priorityServiceModel.To<Priority>();
var priorityResult = await this.repository.AddAsync(priority);
var priorityServiceModelResult = priorityResult.To<PriorityServiceModel>();
return priorityServiceModelResult;
}
The repository method:
public virtual async Task<TEntity> AddAsync(TEntity entity)
{
await this.DbSet.AddAsync(entity);
await this.SaveChangesAsync();
return entity;
}
I expected when the changes had been saved, the returned Priority object to have initialized Project navigation property with the corresponding project instance but the returned object has value as follows:
Priority {
"Id" = 33,
"Name" = "Lowest",
"ProjectId" = "bd15bd0c-fed7-4186-ade0-ad9338dff1d7"
"Project" = null
}
What is the reason for this and how could I receive the project instance initialized in the navigation property Project of newly created and returned Priority object?
Thanks in advance!

Related

Handle POST requests with many-to-many relationships ASP.NET EFCore

I have these classes
public class Project
{
public Guid Id { get; set; }
public string DeployUrl { get; set; }
public List<Technology> Techs { get; set; } = new();
}
public class Technology
{
public Guid Id { get; set; }
public string Name { get; set; }
public string TechImg { get; set; }
[JsonIgnore]
public List<Project> Projects { get; set; } = new();
[JsonIgnore]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Which creates a many-to-many relationship. I want to add an endpoint to create a Project. The main issue here is the approach with which i'll do it. Since I don't send the full Technology object to the frontend, it would be impossible to send back a full Project as the request body for the endpoint since its Technology property would be missing fields, which posses an incompatibility with the actual entity in my server. Of course that the Technology instance already exists. So, what is the best approach here? Currently I'm sending a Project DTO that doesn't contain a List<Technology> but a List<Guid> of technologies, and in my repository I fetch from the DB all those technologies and add them manually. Is this correct? Thanks in advance.
public class ProjectPostDto
{
public string DeployUrl { get; set; }
public List<Guid> TechsIds { get; set; } = new();
}
public async Task<ActionResult> PostProject(ProjectPostDto projectDto)
{
try
{
Project newProject = FromPostDto(projectDto);
newProject.Techs = await Service.GetTechs(projectDto.TechsIds);
await Service.Create(newProject);
ProjectPostDto newProjectDto = ToPostDto(newProject);
return CreatedAtAction(nameof(GetProject), new { id = newProject.Id }, newProjectDto);
} catch (Exception)
{
return StatusCode(500, "Server error");
}
}

Can't Figure Out How To Use AutoMapper With Post Action In RESTful Api

I have a simple RESTful API and this is the post route handler I'm trying to apply AutoMapper in:
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> CreateHotel([FromBody]Hotel hotelCreateDto)
{
var hotel = _mapper.Map<Hotel>(hotelCreateDto);
var createdHotel = await _hotelService.CreateHotel(hotel);
var hotelReadDto = _mapper.Map<HotelReadDto>(createdHotel);
return CreatedAtAction("GetHotelById", new { id = hotelReadDto.Id }, hotelReadDto);
}
So in the request I get a hotelCreateDto which looks like that:
public class HotelCreateDto
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and I map this to Hotel entity:
public class Hotel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and a new hotel object is created in the next line. However when hotelReadDto is going to be assigned to the new mapped object, a 500 error occurs: "AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping."
Could you catch a mistake here? I don't know where I'm doing wrong.
Edit: there'S also this things after the error above: "Mapping types:
Object -> HotelReadDto
System.Object -> HotelFinder.DTO.DTOs.HotelReadDto"
Edit2: Here is the code in the Configure Services:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
And in the Profile class:
public class HotelProfile : Profile
{
public HotelProfile()
{
CreateMap<Hotel, HotelReadDto>();
CreateMap<HotelCreateDto, Hotel>();
}
}
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
and your dto be like this (map hotel to HotelDto):
public class HotelCreateDto : IMapFrom<HotelCreateDto>
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<Hotel,HotelCreateDto>();
}
}

Xamarin Forms SQLLite add OneToOne and OneToMany subobjects to db

Im trying to create a persistent sqllite db (creation tables once you install the app, deletion of db when you uninstall app)
I have a issue that I cant save my sub objects for example
public class ObjectInstanceResponseModel : GenericResponseModel
{
public ObservableCollection<ObjectInstanceData> ObjectInstances { get; set; }
}
public class ObjectInstanceData : GenericResponseModel
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[JsonProperty("idObjectinstance")]
public int IdObjectInstance { get; set; }
[JsonProperty("objectclass_idObjectclass")]
public int ObjectClassIdObjectClass { get; set; }
[JsonProperty("objectclassname")]
public string ObjectClassName { get; set; }
[JsonProperty("visibilitylevel")]
public int VisibilityLevel { get; set; }
[JsonProperty("showname")]
public bool ShowName { get; set; }
[JsonProperty("showicon")]
public bool ShowIcon { get; set; }
[JsonProperty("creationtime")]
public DateTimeOffset CreationTime { get; set; }
[JsonProperty("users_idUsers")]
public int UsersIdUsers { get; set; }
[JsonProperty("isfavorite")]
public bool? IsFavorite { get; set; }
[OneToMany("ObjectInstanceDataStrings")]
[JsonProperty("strings")]
public List<String> Strings { get; set; }
}
public class String
{
[PrimaryKey, AutoIncrement]
[JsonProperty("idObjectparameterstring")]
public int? IdObjectParameterString { get; set; }
[ForeignKey(typeof(ObjectInstanceData))]
public int ObjectInstanceDataStrings { get; set; }
[JsonProperty("stringvalue")]
public string StringValue { get; set; }
[JsonProperty("objectparameterset_idObjectparameterset")]
public int? ObjectParameterSetIdObjectParameterSet { get; set; }
[JsonProperty("showinballon")]
public bool? ShowInBallon { get; set; }
[JsonProperty("idClassparameter")]
public int IdClassParameter { get; set; }
[JsonProperty("classparametername")]
public string ClassParameterName { get; set; }
}
So, my class String is always empty, although there are some rows in the table that I created Strings..
Am I need a lazy loading for this?
I implemented sqllite through depedency service in my app.cs like this:
public partial class App : Application
{
public static SQLiteConnection DatabaseConnection { get; set; }
public App()
{
InitializeComponent();
DatabaseConnection = DependencyService.Get<IConnection>().GetConnection();
CreateTables();
}
private void CreateTables()
{
DatabaseConnection.CreateTable<ObjectInstanceData>();
DatabaseConnection.CreateTable<Models.Objects.String>();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
So, basically the logic should be when no internet, work with sql lite (keep local changes), and when internet come back upload that changes that kept in db, and erase data from tables.
You noticed that Im using response model for api.
So, Im calling from my FavoriteObjectViewModel this:
var response = await ApiServiceProvider.GetObjectInstances(null, true);
and in the ApiServiceProvider:
public static async Task<ObjectInstanceResponseModel> GetObjectInstances(string queryString = null, bool? onlyFavorites = null)
{
HttpResponseMessage response;
response = await apiClient.GetAsync(objectInstancesEndpoint);
var resultContent = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ObservableCollection<ObjectInstanceData>>(resultContent);
objectInstanceResponse.ObjectInstances = result;
//after each api call I need to update db
//delete previous data, and add fresh data from api
App.DatabaseConnection.DeleteAll<ObjectInstanceData>();
foreach (var item in result)
{
App.DatabaseConnection.Insert(item);
if (item.Strings != null && item.Strings.Count > 0)
App.DatabaseConnection.InsertAll(item.Strings);
}
//I only get the data for ObjectInstanceData, Strings model is empty!
var objectsResponseDb = App.DatabaseConnection.GetAllWithChildren<ObjectInstanceData>();
objectInstanceResponse.Succeeded = true;
return objectInstanceResponse;
}
So, my questions are:
If I create tables each time in App.cs is that mean, that I not store data, when user quit application, and re-enter again?
Why is the model Strings empty? When I invoke var strings = App.DatabaseConnection.GetAllWithChildren<Models.Objects.String>(); I can see that there is data?
What is the best approach for doing offline "logging"? maybe there is a some better nuget for sqllite?
I don't know much about persistent databases but one thing I can tell you for sure:you're approach is wrong.
You should create a separate class for the database's logic like creating tables and instantiating the connection along with other methods for manipulating data.
In the App class you can create a static resource of the database class which you can call it and make use of the methods in it.
I'm not very good with explaining but here it is a very basic example of how it should look:https://learn.microsoft.com/en-us/xamarin/get-started/tutorials/local-database/?tutorial-step=2&tabs=vswin.

Running migrations on PersistedGrants for Identity Server 4 fails

I'm trying to set up a new instance of IS4 on .Net Core 3.1, and running an initialization procedure fails as the 'Key' column in the PersistedGrants context seem to be invalid.
This does not occur in .Net 3.0, we tried to replicate, assuming this has something to do with EF Core 3.1, however, I can't see what it might be.
Generated output, if run via Add-Migrations:
I didn't want to implement my own implementation of this DbContext but it seems like that is the route I'd have to go?
Persisted Grant Entity
https://github.com/IdentityServer/IdentityServer4/blob/master/src/EntityFramework.Storage/src/Entities/PersistedGrant.cs
On my project we went the route of adding our own entity to application DbContext:
public class PersistedGrant
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual string Key { get; set; }
public virtual string Type { get; set; }
public virtual string SubjectId { get; set; }
public virtual string ClientId { get; set; }
public virtual DateTime CreationTime { get; set; }
public virtual DateTime? Expiration { get; set; }
public virtual string Data { get; set; }
}
and implementing the persisted store:
public class PersistedGrantStore : IPersistedGrantStore
{
Dal.MasterDbContext _dbContext;
public PersistedGrantStore(Dal.MasterDbContext dbContext)
{
_dbContext = dbContext;
}
private PersistedGrant ToModel(Dal.PersistedGrant entity)
{
return new PersistedGrant()
{
ClientId = entity.ClientId,
CreationTime = entity.CreationTime,
Data = entity.Data,
Expiration = entity.Expiration,
Key = entity.Key,
SubjectId = entity.SubjectId,
Type = entity.Type
};
}
public Task<IEnumerable<PersistedGrant>> GetAllAsync(string subjectId)
{
var grants = _dbContext.PersistedGrants.Where(x => x.SubjectId == subjectId)
.ToList()
.Select(x => ToModel(x));
return Task.FromResult(grants);
}
...

Created and Modified date issue

I was practicing User.Identity and timestamps functions in ASP.NET MVC 5,
So I created a student class filled some properties, I just wanted to test if it is capturing timestamps and userId, so user id is getting captured and datetime too, problem is whenever I'm editing a record and save it, its created date becomes Null and modified date is updated, please review the code and help.
Thanks in advance.
Below is the Code
{
public class BaseEntity
{
public DateTime? DateCreated { get; set; }
public string UserCreated { get; set; }
public DateTime? DateModified { get; set; }
public string UserModified { get; set; }
}
public class Student : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Subject { get; set; }
public string Class { get; set; }
public Section Section { get; set; }
public byte SectionId { get; set; }
}
then I used Codefirst approach and created an application Database and added this code in Identity Model
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Student> Students { get; set; }
public override int SaveChanges()
{
AddTimestamps();
return base.SaveChanges();
}
//public override async Task<int> SaveChangesAsync()
//{
// AddTimestamps();
// return await base.SaveChangesAsync();
//}
private void AddTimestamps()
{
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));
var currentUsername = !string.IsNullOrEmpty(System.Web.HttpContext.Current?.User?.Identity?.Name)
? HttpContext.Current.User.Identity.Name
: "Anonymous";
foreach (var entity in entities)
{
if (entity.State == EntityState.Added)
{
((BaseEntity)entity.Entity).DateCreated = DateTime.UtcNow;
((BaseEntity)entity.Entity).UserCreated = currentUsername;
}
else
((BaseEntity)entity.Entity).DateModified = DateTime.UtcNow;
((BaseEntity)entity.Entity).UserModified = currentUsername;
}
}
public DbSet<Section> Sections { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I have created a simple controller with create,edit and dispay actions.
The code you posted doesn't show DateCreated being set to null as far as I can see. I think the issue is when you save an existing record you do not have the DateCreated or UserCreated fields in your view. So when you post the form the MVC model binder doesn't see them and thus sets them to null (I'm assuming your are binding to the Student model in your controller action).
In your edit view add the following hidden fields:
#Html.HiddenFor(model => model.DateCreated)
#Html.HiddenFor(model => model.UserCreated)
Now when you post the form the MVC model binder will bind these values to your model and save them to the database.

Resources