I upgraded from 0.21 to version 2 and this code used to work.
Give this code
var log = new NotificationLog()
{
TenantId = tenantId,
};
log.Details.Add(new NotificationLogDetail()
{
NotificationType = "Email",
NotificationName = template.Event + " (" + template.Role + ")",
NotificationTag1 = @event.ContentId.ToString(),
To = userInfo.User.Email
});
await _notificationLogRepo.InsertAsync(log);
Throws the following exception
The instance of entity type 'NotificationLogDetail' cannot be tracked because another instance with the key value '{Id: 00000000-0000-0000-0000-000000000000}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I don't know if this is the intended behavior but I had to change
public class NotificationLogDetail : CreationAuditedEntity<Guid>
{
public string NotificationType { get; set; }
public string NotificationName { get; set; }
public string NotificationTag1 { get; set; }
public string NotificationTag2 { get; set; }
public string To { get; set; }
}
to
public class NotificationLogDetail : CreationAuditedEntity<Guid>
{
public override Guid Id { get; protected set; } = Guid.NewGuid(); <------
public string NotificationType { get; set; }
public string NotificationName { get; set; }
public string NotificationTag1 { get; set; }
public string NotificationTag2 { get; set; }
public string To { get; set; }
}
And now it works. Is this the intended behavior or is it a bug? Thank you!
@wocar
Maybe you can try it like in below CrudAppService Code:
I think Id should be set by code ,
protected virtual void SetIdForGuids(TEntity entity)
{
var entityWithGuidId = entity as IEntity<Guid>;
if (entityWithGuidId == null || entityWithGuidId.Id != Guid.Empty)
{
return;
}
EntityHelper.TrySetId(
entityWithGuidId,
() => GuidGenerator.Create(),
true
);
}
by the way, TenantId is the same
protected virtual void TryToSetTenantId(TEntity entity)
{
if (entity is IMultiTenant && HasTenantIdProperty(entity))
{
var tenantId = CurrentTenant.Id;
if (!tenantId.HasValue)
{
return;
}
var propertyInfo = entity.GetType().GetProperty(nameof(IMultiTenant.TenantId));
if (propertyInfo == null || propertyInfo.GetSetMethod(true) == null)
{
return;
}
propertyInfo.SetValue(entity, tenantId);
}
}
Entities should have a .ctor method to set Id and other properties, please follow abp docs:
https://docs.abp.io/en/abp/latest/Entities#aggregate-example
@gdlcf88
Thanks for remind.
and how about tenantId ,should I set it by ICurrentTenant.Id myself even client is Authorization
and how about tenantId ,should I set it by
ICurrentTenant.Idmyself even client is Authorization
I will manually set TenantId = ICurrentTenant.Id for new entity, and if you are in ApplicationService or DomainService, the base class property CurrentTenant has been set by abp framework, you do not have to inject it manually.
This is EF Core error I suppose.
Always set GUID Ids as best practice.
See https://github.com/abpframework/abp/blob/dev/docs/en/Entities.md#entities-with-guid-keys
Also, always set TenantId manually (because ABP may not know what was the tenantid when you have created the entity - you know you can change tenantid before save the entity).
Thanks for the reply @hikalkan so bottom line is that I have to set the nested collection鈥檚 items IDs in the constructor?
Did I understand correctly?
Yes, set on their constructor (use IGuidGenerator). Not only for nesteds, for all of your entities / aggregate roots.
Thanks!! Love your work guys keep it up!!
Most helpful comment
Yes, set on their constructor (use IGuidGenerator). Not only for nesteds, for all of your entities / aggregate roots.