efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Support fixed-size collections

Open uladz-zubrycki opened this issue 4 years ago • 12 comments

Description

Error message is not that descriptive as it could be for a case when fixed size collection (Array) is used for a navigation property. It'd be nice to be pointed to the problematic navigation property or entity type at least with a clean problem description in the error message.

Creating issue because of the conversation happening here https://github.com/dotnet/efcore/issues/6589#issuecomment-689041161

Could the exception message be more intuitive, please? It took me a while to find the cause of this problem, whereas if it had said something different I might have got it quicker "Association property instances cannot be a fixed length type, such as an Array" Note how I said "instances" so as not to mislead people into thinking they cannot declare the property type as IEnumerable<T>

This issue is very old. If you're still seeing this exception with EF Core 3.1 or 5.0, then please open a new issue and attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

Code

I reproduced the issue on a sample used for another issue in this repo previously, so it's not that minimal (and won't probably work even, when this fixed collection case is improved, due to another configuration error), but should be okay for the error discussed. Repro.zip

Include provider and version information

EF Core version: 3.1.12 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: net48 Operating system: Windows 10 IDE: Visual Studio 2019 16.3

uladz-zubrycki avatar Mar 24 '21 11:03 uladz-zubrycki

Note for triage:

Unhandled exception. System.NotSupportedException: Collection was of a fixed size.
   at System.SZArrayHelper.Remove[T](T value)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor`3.Remove(Object entity, Object value)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.RemoveFromCollection(INavigation navigation, InternalEntityEntry value)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.RemoveFromCollection(InternalEntityEntry entry, INavigation navigation, InternalEntityEntry value)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IReadOnlyList`1 containingPrincipalKeys, IReadOnlyList`1 containingForeignKeys, Object oldValue, Object newValue)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IReadOnlyList`1 keys, IReadOnlyList`1 foreignKeys, Object oldValue, Object newValue)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectKeyChange(InternalEntityEntry entry, IProperty property)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.PropertyChanged(InternalEntityEntry entry, IPropertyBase propertyBase, Boolean setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.PropertyChanged(InternalEntityEntry entry, IPropertyBase property, Boolean setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete, CurrentValueType valueType)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetTemporaryValue(IProperty property, Object value, Boolean setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.SetForeignKeyProperties(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, Boolean setModified, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.FixupToPrincipal(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, Boolean setModified, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.DelayedFixup(InternalEntityEntry entry, INavigation navigation, InternalEntityEntry referencedEntry, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.InitialFixup(InternalEntityEntry entry, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.FireStateChanged(EntityState oldState)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.Add[TEntity](TEntity entity)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Add(TEntity entity)
   at Repro.Program.Main(String[] args) in C:\Stuff\AllTogetherNow\ThreeOne\ThreeOneApp.cs:line 23

ajcvickers avatar Mar 26 '21 15:03 ajcvickers

Putting this on the backlog to check the first time we create a property accessor. This won't help in all cases because the property can be assigned a new instance of a different type at any time, and we don't want to do an additional check every time we access the property.

ajcvickers avatar Mar 29 '21 14:03 ajcvickers

Hi, I have exactly the same problem on the latest version.

Include provider and version information

EF Core version: 5.0.10 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .Net Core 5.0 Operating system: Windows 10 IDE: Visual Studio 2019 16.11.3

Here is the log: System.NotSupportedException: Collection was of a fixed size. at System.SZArrayHelper.Add[T](T value) at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor`3.Add(Object entity, Object value, Boolean forMaterialization) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.AddToCollection(InternalEntityEntry entry, INavigationBase navigation, InternalEntityEntry value, Boolean fromQuery) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.ToDependentFixup(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, Boolean fromQuery) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.InitialFixup(InternalEntityEntry entry, Boolean fromQuery) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.FireStateChanged(EntityState oldState) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityStateAsync(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintActionAsync(EntityEntryGraphNode`1 node, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraphAsync[TState](EntityEntryGraphNode`1 node, Func`3 handleNode, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.AddRangeAsync(IEnumerable`1 entities, CancellationToken cancellationToken) at WatchMyGame.Web.Dal.Services.ImportDataService.UpdateProjectsAsync(User user, String projectData, Boolean isManager, IDictionary`2 projects, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web.Dal\Services\ImportDataService.cs:line 238 at WatchMyGame.Web.Dal.Services.ImportDataService.UpdateUserAsync(Int64 seasonId, User user, UserSeason userSeason, ImportAnagraficaItemModel anagrafica, IDictionary`2 projects, UserImportConfirmRequest config, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web.Dal\Services\ImportDataService.cs:line 306 at WatchMyGame.Web.Dal.Services.ImportDataService.ProcessUserAsync(Int64 seasonId, User user, UserSeason userSeason, ImportAnagraficaItemModel anagrafica, IDictionary`2 projects, UserImportConfirmRequest config, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web.Dal\Services\ImportDataService.cs:line 352 at WatchMyGame.Web.Dal.Services.ImportDataService.ConfirmImportAnagraficaAsync(Int64 userId, Int64 seasonId, UserImportConfirmRequest config, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web.Dal\Services\ImportDataService.cs:line 94 at WatchMyGame.Web.Services.ImportService.ConfirmImportAnagraficaAsync(Int64 userId, Int64 seasonId, UserImportConfirmRequest request, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web.Services\Services\ImportService.cs:line 117 at WatchMyGame.Web.Controllers.Api.ImportController.ConfirmAnagraficaAsync(UserImportConfirmRequest request, CancellationToken cancellationToken) in C:\Lavoro\Git\WatchMyGame\Src\WatchMyGame.Web\Controllers\Api\ImportController.cs:line 86 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

FrankIceBass avatar Sep 18 '21 09:09 FrankIceBass

@FrankIceBass Fixed-size collections, such as arrays, are not supported since EF needs to add elements to them.

ajcvickers avatar Sep 18 '21 09:09 ajcvickers

@ajcvickers I hardly understand which is the fixed-size collections. I'm adding items to a DbSet with AddRangeAsync. I'm using just this on DbSet:

await this.context.ProjectUsers.AddRangeAsync(list, cancellationToken);

FrankIceBass avatar Sep 18 '21 09:09 FrankIceBass

@FrankIceBass Typically a collection of one entity type on another entity type. For example, the collection of posts on a blog. This is known as a navigation. See Relationships in the docs.

ajcvickers avatar Sep 18 '21 09:09 ajcvickers

@ajcvickers I really hardly understand. I'm using scaffolded db context and it is just a 1-N table. I will try to check more why this "feature" is annoying me so much. By the way I have the same exception even if i try to update objects into that DbSet.

Anyway.. thank you for your support.

FrankIceBass avatar Sep 18 '21 09:09 FrankIceBass

@FrankIceBass Feel free to open a new issue attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

ajcvickers avatar Sep 18 '21 09:09 ajcvickers

@FrankIceBass I'd guess that you are using sth like Array to initialize the navigation property from 1 to N for your relation somewhere during the entity instance initialization.

See the repro I attached initially, it might shed some light.

uladz-zubrycki avatar Sep 18 '21 09:09 uladz-zubrycki

@v-zubritsky unfortunately it's not my case.

I have the same exception even trying to add items not related to parent entities.

var list = projectList.Where(item => !userProjects.Where(y => y.ProjectId == item.Id).Any()) .Select(x => new Ef.ProjectUser { CreationDate = DateTime.UtcNow, Deleted = false, IsManager = isManager, LastUpdate = DateTime.UtcNow, ProjectId = x.Id, UserId = user.Id }).ToList();

            await this.context.ProjectUsers.AddRangeAsync(list, cancellationToken);

FrankIceBass avatar Sep 18 '21 09:09 FrankIceBass

Hi everyone, I just solved it... well... it's strange solution but it worked for me. I was watching this.context.ChangeTracker.Entries() and it was full of unchanged properties. I just added this.context.ChangeTracker.Clear() before running the code I posted above... well... it worked...

FrankIceBass avatar Sep 18 '21 16:09 FrankIceBass

Calling .ToList() solved the problem for me. I was getting the above error message ( Collection was of a fixed size. at System.SZArrayHelper.Remove...) , when I was setting model.Foos = newFoos; only when model.Foos count was 1 and newFoos was more than 1 or the other way around.

omuleanu avatar Aug 04 '22 20:08 omuleanu

I feel the need to weigh in here, having encountered this error and scratched my head for over an hour as to where the fixed array was, why it updated and why it was failing to 'Remove' with an 'Add'.

Firstly, the exception misled me because I was performing a .Add(entity1) followed by a .Remove(entity2). The exception was being thrown on the add.

Secondly, the fixed array was not in either entity1 or entity2. My schema looked like the following:

[Table]
public class A
{
  [Key]
  public int AId { get; set; }
  public IList<B>? Bs { get; set; } = new List<B>();
}

[Table]
public class B
{
  public int AId { get; set; }
  public int CId { get; set; }
  [ForeignKey("AId")]
  public IList<A> A { get; set; }
  [ForeignKey("CId")]
  public IList<C> C { get; set; }
}


[Table]
public class C
{
  [Key]
  public int CId { get; set; }
  
  //I'd set as an array to avoid updating from here.
  public IEnumerable<B> Bs { get; set; } = Array.Empty<B>();
}

Then when trying to add an A, with a bunch of Bs in the list, I believe in the background it wanted to update the Array of Cs through the change tracking.

In brief:

MyDbContext db = new(options);

//Perhaps I loaded some Cs earlier on in the same DbContext, bringing them into the local change tracking, perhaps why clearing the change tracking has worked for others
var bunchOfCs = db.Cs.ToList();

A myA = new();
myA.Bs.Add(new B() { AId = 123, BId = 234 });

db.As.Add(myA); //throws error

Changing the C.Bs to a List allowed it to run.

In conclusion, it might be a good idea to add a warning of this type of background behaviour along with the exception.

rombethor avatar Jan 31 '23 23:01 rombethor

@FrankIceBass Fixed-size collections, such as arrays, are not supported since EF needs to add elements to them.

How is it not possible to take a fixed sized array and convert it into an array that is growable and not fixed?

FreddyJohn avatar Apr 08 '24 17:04 FreddyJohn

How is it not possible to take a fixed sized array and convert it into an array that is growable and not fixed?

Arrays in C#/.NET are always fixed size. List<T>, for example, can grow as required.

cincuranet avatar Apr 08 '24 18:04 cincuranet