Adding new Entity generates negative id - .net-core

I'm trying to add a new Container entity, but when I add the entity, I'm getting a negative Id being displayed in the resulting dataset. However, inside of my SQL database, I end up getting the correct next Id of the Container that was just inserted.
I've looked around, and I can see where this same issue occurs if you were to Update the entity before adding it - but I can't quite see anything related to the error occuring when you're first Adding the entity.
I'm literally just passing a new'ed up Container object, and adding it to the Containers DbSet on the ProductContext.
public class ProductService : IProductService
public ProductService(IProductContext productContext)
{
_productContext = productContext;
}
public Container CreateContainer(Container container, int currentUserId)
{
_productContext.Containers.Add(container);
_productContext.SaveChangesAsync();
return container;
}
}
However, when stepping through, I can see that I start off with a zero'd out Id...
And after the save is processed, the container has an Id of -2147482645.
But if I check the database, I end up with the correct next value after it's been saved. However, this value does not update on the entity, instead it stays as the -2147482645 value.
Here's my Container entity...
public class Container : BaseEntity
{
public Container()
{
this.ChildContainers = new HashSet<Container>();
this.InventoryItems = new HashSet<InventoryItem>();
}
public ICollection<Container> ChildContainers { get; set; }
public ICollection<InventoryItem> InventoryItems { get; set; }
public Location Location { get; set; }
public int? LocationId { get; set; }
public string Name { get; set; }
[ForeignKey("ParentContainerId ")]
public Container ParentContainer { get; set; }
public int? ParentContainerId { get; set; }
}
and the value of the BaseEntity class
public abstract class BaseEntity
{
[Key]
public virtual int Id { get; set; }
[DefaultValue(false)]
public virtual bool IsDeleted { get; set; }
protected virtual object Actual => this;
public override bool Equals(object obj)
{
if (!(obj is BaseEntity other))
return false;
if (ReferenceEquals(this, other))
return true;
if (Actual.GetType() != other.Actual.GetType())
return false;
if (Id == 0 || other.Id == 0)
return false;
return Id == other.Id;
}
public static bool operator ==(BaseEntity a, BaseEntity b)
{
if (a is null && b is null)
return true;
if (a is null || b is null)
return false;
return a.Equals(b);
}
public static bool operator !=(BaseEntity a, BaseEntity b)
{
return !(a == b);
}
public override int GetHashCode()
{
return (Actual.GetType().ToString() + Id).GetHashCode();
}
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
And the implementation of my SaveChangesAsync method - however, I don't believe this really could affect it, as the Id is getting updated before it hits this point.
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
var entries = ChangeTracker
.Entries()
.Where(e => e.Entity is BaseEntity && (
e.State == EntityState.Added
|| e.State == EntityState.Modified));
foreach (var entityEntry in entries)
{
((BaseEntity)entityEntry.Entity).UpdatedDate = DateTime.Now;
if (entityEntry.State == EntityState.Added)
{
((BaseEntity)entityEntry.Entity).CreatedDate = DateTime.Now;
}
}
return await base.SaveChangesAsync();
}
EDIT 1: SaveChanges syncronously
It was mentioned that the SaveChanges could affect it as I was running the asynchronous version of the method in a synchronous way. I don't believe this would be causing the error, as the Id is getting set/updated inside the Add method.

Related

Ensure sensitive data is removed from response [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 11 months ago.
Improve this question
I have some dtos returned by my API that have sensitive data fields like Createdby, CreatedDate, LastModifiedBy and LastModifiedDate. These fields should only be in the response if the user is authenticated and/or have allowed roles in his claims. My current running solution is to use my dto mappers that have a dependency on IUserIdentity (custom interface).
UserDto record
public record UserDto {
public string? CreatedBy { get; init; }
public DateTime? CreatedDate { get; init; }
public string? Email { get; set; }
public string? ExternalProviderUserId { get; set; }
public string? GivenName { get; set; }
public Guid? Id { get; set; }
public string? LastModifiedBy { get; init; }
public DateTime? LastModifiedDate { get; init; }
public string? Name { get; set; }
public string? Surname { get; set; }
}
UserDtoMapper class
public class UserDtoMapper : IUserDtoMapper {
private readonly IUserIdentity _userIdentity;
public UserDtoMapper(IUserIdentity userIdentity) {
_userIdentity = userIdentity;
}
public UserDto ToDto(User user) => new() {
CreatedBy = _userIdentity.IsAuthenticated ? user.CreatedBy : null,
CreatedDate = _userIdentity.IsAuthenticated ? user.CreatedDate : null,
Email = user.Email,
ExternalProviderUserId = user.ExternalProviderUserId,
GivenName = user.GivenName,
Id = user.Id,
LastModifiedBy = _userIdentity.IsAuthenticated ? user.LastModifiedBy : null,
LastModifiedDate = _userIdentity.IsAuthenticated ? user.LastModifiedDate : null,
Name = user.Name,
Surname = user.Surname
};
public List<UserDto> ToDtos(IEnumerable<User> users) {
return users.Select(o => ToDto(o)).ToList();
}
}
It work as entended but I would like to have a global and easier way to set these fields as senstitive and let the api filter them if the user is not authenticed and/or doesn't have allowed roles in his claims. After searching online for many days and did try and error solutions, I finally comes with my own solution and hope to have feedbacks on potential issues I could have. It works great so far.
My solution is to use the Filters in ASP.NET Core and Reflection (C#).
I've created this custom attribute SensitiveDataAttribute
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class SensitiveDataAttribute : Attribute {
public SensitiveDataAttribute() {
AllowedRoles = Array.Empty<string>();
}
public SensitiveDataAttribute(params string[] allowedRoles)
: this((IEnumerable<string>)allowedRoles) { }
public SensitiveDataAttribute(IEnumerable<string> allowedRoles) {
if (allowedRoles == null) {
throw new ArgumentNullException(nameof(allowedRoles));
}
if (!allowedRoles.Any()) {
throw new InvalidOperationException("At least one role must be specified.");
}
AllowedRoles = allowedRoles;
}
/// <summary>
/// Gets the collection of allowed roles.
/// </summary>
public IEnumerable<string> AllowedRoles { get; }
public Task<bool> IsValidAsync(ClaimsPrincipal user) {
bool isUserAuthenticated = user.Identity?.IsAuthenticated ?? false;
if (!isUserAuthenticated) {
return Task.FromResult(false);
}
if (!AllowedRoles.Any()) {
return Task.FromResult(true);
}
bool found = AllowedRoles.Any(r => user.IsInRole(r));
return Task.FromResult(found);
}
public override string ToString() {
if (!AllowedRoles.Any()) {
return $"{nameof(SensitiveDataAttribute)}:User must be authenticated";
}
string roles = string.Join("|", AllowedRoles);
var stringValue = $"User must be authenticated and User.IsInRole must be true for one of the following roles:({roles})";
return $"{nameof(SensitiveDataAttribute)}: {stringValue}";
}
}
And created this action filter SensitiveDataActionFilter
public class SensitiveDataActionFilter : IAsyncActionFilter {
private readonly ILogger<SensitiveDataActionFilter> _logger;
public SensitiveDataActionFilter(ILogger<SensitiveDataActionFilter> logger) {
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext _, ActionExecutionDelegate next) {
ActionExecutedContext executedContext = await next();
if (executedContext.Exception != null) {
return;
}
if (executedContext.Result is not OkObjectResult result) {
return;
}
if (result.Value == null) {
return;
}
_logger.LogDebug("Filtering action result sensitive data of type {Type} started.", result.Value.GetType());
Stopwatch stopwatch = Stopwatch.StartNew();
if (result.Value is IEnumerable elements) {
int index = 0;
foreach (var element in elements) {
await FilterResultAsync(executedContext, element, $"[{index++}].");
}
_logger.LogDebug("Filtering action result sensitive data of type {Type} finished in {Elapsed} ms.", result.Value.GetType(), stopwatch.ElapsedMilliseconds);
return;
}
await FilterResultAsync(executedContext, result.Value, null);
_logger.LogDebug("Filtering action result sensitive data of type {Type} finished in {Elapsed} ms.", result.Value.GetType(), stopwatch.ElapsedMilliseconds);
}
private async Task FilterResultAsync(ActionExecutedContext context, object? source, string? propPath) {
if (source == null) {
return;
}
PropertyInfo[] properties = source.GetType()
.GetProperties(BindingFlags.Instance
| BindingFlags.Public)
.Where(p => p.GetMethod != null
&& p.GetMethod.IsPublic
&& p.GetMethod.IsStatic == false)
.ToArray();
foreach (var propertyInfo in properties) {
if (propertyInfo == null) {
continue;
}
object? propertyValue = propertyInfo.GetValue(source);
string propertyName = propertyInfo.Name;
string path = $"{propPath}{propertyName}";
if (propertyValue == null) {
_logger.LogDebug("Property {Path}: {Value}", path, propertyValue);
continue;
}
SensitiveDataAttribute? sensitiveDataAttribute = propertyInfo
.GetCustomAttribute<SensitiveDataAttribute>(true);
if (sensitiveDataAttribute != null) {
_logger.LogDebug("Property {Path} is sensitive: {Value}", path, propertyValue);
bool isValid = await sensitiveDataAttribute.IsValidAsync(context.HttpContext.User);
if (!isValid) {
_logger.LogDebug("Property {Path} to be cleared: {Reason}", path, sensitiveDataAttribute);
propertyInfo.SetValue(source, default);
continue;
}
}
if (propertyValue is DateTime or string) {
_logger.LogDebug("Property {Path}: {Value}", path, propertyValue);
continue;
}
if (propertyValue is IEnumerable elements) {
int index = 0;
foreach (var element in elements) {
await FilterResultAsync(context, element, $"{path}.[{index++}].");
}
continue;
}
_logger.LogDebug("Property {Path}: {Value}", path, propertyValue);
await FilterResultAsync(context, propertyValue, $"{path}.");
}
}
}
And register the filter SensitiveDataActionFilter like this:
services.AddScoped<SensitiveDataActionFilter>();
services.AddControllers(options => options.Filters.AddService<SensitiveDataActionFilter>())
And add the attribute SensitiveDataAttribute to sensitive field:
public record UserDto {
[SensitiveData]
public string? CreatedBy { get; init; }
[SensitiveData]
public DateTime? CreatedDate { get; init; }
public string? Email { get; set; }
[SensitiveData("SYS_ADMIN")]
public string? ExternalProviderUserId { get; set; }
public string? GivenName { get; set; }
public Guid? Id { get; set; }
[SensitiveData]
public string? LastModifiedBy { get; init; }
[SensitiveData]
public DateTime? LastModifiedDate { get; init; }
public string? Name { get; set; }
public string? Surname { get; set; }
}
Circular reference is one problem I have have with my code. I might need to have a max-depth setting somewhere. I had problem with DateTime and string. Datetime gave me a circular reference issue and I needed to check if propertyValue is DateTime then stopped to go deeper. I needed to do the same with string because string implements IEnumerable.
Any feedbacks is appreciated. :)
My main argument against this solution is that in case of a bug you're revealing too much information. From a security point of view it much better to structure code so that in case of a bug not enough information is returned.

Delete Child entity Record in EF core after comparing

I have a Child entity of Aggregate Entity with one-to-many relation, and in the child, there is a list containing the parent id. the data coming from the front-end is a list of object and if it is no different from what comes to back-end I will do nothing with it. otherwise, I will remove what has been removed and add what has been added to the table.
I am new at EF CORE and I am trying to apply this login in this relation.
if (child.list.SuccessorId == parent.vehicleCategoryId) => ignore;
if(!child.list.contain(parent.vehicleCategoryId)
remove(parent.vehicleCategoryId) => //delete record with vehicleCategoryId
else
add(child)
here is my entities.
public class VehicleCategory : LookupAggregateRoot<VehicleCategory>
{
#region Constructor
private VehicleCategory()
{
_successors = new List<VehicleSuccessorCategory>();
}
#endregion
#region Data
public virtual LocalizedText Name { get; set; }
public virtual long Sequence { get; set; }
private readonly List<VehicleSuccessorCategory> _successors;
public IEnumerable<VehicleSuccessorCategory> Successors
{
get => _successors.AsReadOnly();
set => throw new NotImplementedException();
}
#endregion
#region Behaviour
public void AddSuccessor(VehicleSuccessorCategory entrySuccessorCategory)
{
_successors.Add(entrySuccessorCategory);
}
public void RemoveSuccessor(VehicleSuccessorCategory entrySuccessorCategory)
{
_successors.Remove(entrySuccessorCategory);
}
}
public class VehicleSuccessorCategory : ID365Entity<int>, IEnumerable
{
#region Constructor
public int Id { get; set; }
public int SuccessorId { get; set; }
public VehicleSuccessorCategory(int order)
{
Order = order;
}
#endregion
#region Data
public int Order { get; set; }
#endregion
public bool IsTransient()
{
throw new NotImplementedException();
}
public IEnumerator GetEnumerator()
{
yield return Id;
yield return Order;
}
}
I tried
VehicleCategory vehicleCategory = _genericRepository.Get(aggregate.Id);
foreach (var successorCategory in aggregate.Successors)
{
var successorCategoryToRemove =
vehicleCategory.Successors.Where(e => e.SuccessorId == successorCategory.SuccessorId);
foreach (var vehicleSuccessorCategory in successorCategoryToRemove)
vehicleCategory.RemoveSuccessor(vehicleSuccessorCategory);
}

Xamarin Setting default Boolean value

I am using sqlite-net-pcl and adding a new column to a database DTO and I wanted to set the default value to true and then once I have update the data it would update to the correct value. But the default is not working for me in xamarin.
is there any other way to do this?
[NotNull]
public boolean Istaxable { get; set; } = true;
This will block me from doing a update.
[NotNull, Default(value: true)]
Error default is unknown
DTO
public class DtoTaxableLink
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[NotNull]
public bool IsTaxable { get; set; } = true;
}
service
await App.LocalDB.InsertTaxableLinksAsync(BuildDbTaxableLinkItem( public Task<int> InsertTaxableLinksAsync(List<DtoTaxableLink> taxableLinks)
ListResponse.Data));
local db
public Task<int> InsertTaxableLinksAsync(List<DtoTaxableLink> taxableLinks)
{
return database.InsertAllAsync(taxableLinks, true);
}
Helper
private static List<DtoTaxableLink> BuildDbTaxableLinkItem(List<TaxablelineLink> taxableLinks)
{
List<DtoTaxableLink> dtoTaxableLink= new List<DtoTaxableLink>();
foreach (var taxink in taxableLinks)
{
DtoTaxableLink dtoTaxableLink= new DtoTaxableLink();
dtoTaxableLink.IsTaxable = taxableLinks.IsTaxable ;
dtoTaxableLink.Add(dtoTaxableLink);
}
return dtoTaxableLink;
}
According to your description, you want to set the default value to true when using sqlite-net-pcl and adding a new column to a database.
You can do it through property itself, field default value not going change until another value going to set.Please take a look the following code:
public class User
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string userName { get; set; }
public string password { get; set; }
private bool _sel = true;
[NotNull]
public bool Selected
{
get { return _sel; }
set { _sel = value; }
}
}
Now you can see I set Selected property default value is True, then you can update this value that you want.

Clean way for updating object in a collection of abstract objects

As I'm developping an asp net core + ef core 2.0 with localized objects in my model, I adapted the solution provided in the following link to localize my objects link.
I'm now trying to find a clean way to update my collection of translation when updated object are received in the controller.
For the moment I have a step model class defined this way :
public class Step
{
//Native properties
public Guid ID { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public string ScriptBlock { get; set; }
//Parent Step Navigation property
public Nullable<Guid> ParentStepID { get; set; }
public virtual Step ParentStep { get; set; }
//Collection of sub steps
public virtual ICollection<Step> SubSteps { get; set; }
//MUI Properties
public TranslationCollection<StepTranslation> Translations { get; set; }
public string Description { get; set; }
//{
// get { return Translations[CultureInfo.CurrentCulture].Description; }
// set { Translations[CultureInfo.CurrentCulture].Description = value; }
//}
public Step()
{
//ID = Guid.NewGuid();
Translations = new TranslationCollection<StepTranslation>();
}
}
public class StepTranslation : Translation<StepTranslation>
{
public Guid StepTranslationId { get; set; }
public string Description { get; set; }
public StepTranslation()
{
StepTranslationId = Guid.NewGuid();
}
}
Translation and translationCollection are the same as in the link
public class TranslationCollection<T> : Collection<T> where T : Translation<T>, new()
{
public T this[CultureInfo culture]
{
// indexer
}
public T this[string culture]
{
//indexer
}
public bool HasCulture(string culture)
{
return this.Any(x => x.CultureName == culture);
}
public bool HasCulture(CultureInfo culture)
{
return this.Any(x => x.CultureName == culture.Name);
}
}
public abstract class Translation<T> where T : Translation<T>, new()
{
public Guid Id { get; set; }
public string CultureName { get; set; }
protected Translation()
{
Id = Guid.NewGuid();
}
public bool HasProperty(string name)
{
return this.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Any(p => p.Name == name);
}
}
My issue in this sample is how to deal correctly with the PUT method and the Description property of my step controller. When it receive a Step object to update (which is done through a native c# client) only the string Description property of Step might have been created/updated/unchanged. So I have to update/create/do Nothing on the Description of the translation in the correct culture.
My first guess is to add in the TranslationCollection class a method in which I could pass the culture, the name of the property to update or not (Description in this case) and the value of the Description.
But as the TranslationCollection is a collection of abstract objects I don't even if this is a good idea and if it's possible.
If someone would have any advice on it (hoping I was clear enough) it would be great !
Finally answered my own question, and it was quite simple.
Just had to use the indexer like :
myobject.Translations[userLang].Name = value;

Add checking in controller

I have class User in my project and have model UserRow (for showing user in view)
it's UserRow
using System;
namespace Argussite.SupplierServices.ViewModels
{
public class UserRow
{
public Guid Id { get; set; }
public string FullName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Status { get; set; }
public int Role { get; set; }
public Guid SupplierId { get; set; }
public bool ActionsAllowed { get; set; }
public bool MailResendRequired { get; set; }
}
}
and I need to add in my controller checking if ActionsAllowed
[HttpPost]
public ActionResult Unlock(Guid id)
{
var user = Context.Users.Find(id);
if (user == null)
{
return Json(CommandResult.Failure("User was not found. Please, refresh the grid and try again."));
}
var checkActionsAllowed = Context.Users.AsNoTracking()
.Select(e => new UserRow
{
Id = e.Id,
ActionsAllowed = e.ActionsAllowed
};
if (checkActionsAllowed == true)
{
user.Status = UserStatus.Active;
return Json(CommandResult.Success(string.Format("User {0} has been unlocked.", user.FullName)));
}
else return;
}
but I got error with ActionsAllowed = e.ActionsAllowed and
in else return;
Help me please to solve this problem.
You have two problems:
Context.Users.AsNoTracking()
.Select(e => new UserRow
{
ActionsAllowed = e.ActionsAllowed
};
returns a list of objects, not a single object.
You have queried the user above, so i guess you can write simply:
if (user.ActionsAllowed) {
user.Status = UserStatus.Active;
return Json(CommandResult.Success...);
}
The second problem is the return; statement.
Your method returns an action result, so you have to return something.
For example
return Json(CommandResult.Failure(
"ActionsAllowed = false"));
First error sounds like you User class doesn't provide a ActionsAllowed Boolean property, while the second error happens because you need to return something from the method that can be interpreted as an ActionResult.
EDIT:
Hmm, I didn't notice this the first time, but this:
var checkActionsAllowed = Context.Users.AsNoTracking()
.Select(e => new UserRow
{
Id = e.Id,
ActionsAllowed = e.ActionsAllowed
};
followed by this:
if (checkActionsAllowed == true)
makes no sense - you're not returning a boolean result from a Select method, but rather an IEnumerable. Perhaps you should add your User schema to your question so that it's more obvious what you're trying to accomplish.

Resources