fullstackhero / blazor-starter-kit

Clean Architecture Template for Blazor WebAssembly Built with MudBlazor Components.
MIT License
3.48k stars 730 forks source link

Computed column (NotMapped) exception #333

Closed dotnetshadow closed 2 years ago

dotnetshadow commented 2 years ago

Describe the bug If my domain object has a computed column that isn't mapped to the database it generates an error when trying to access that value

To Reproduce Steps to reproduce the behavior:

  1. Create a domain object such as

    public class Product: AuditableEntity<int>
    {
    public decimal Price { get; set; }
    
    [NotMapped]
    public decimal PriceTaxed => Price * 1.1m;
    
    // some other properties
    }
  2. For your query

    Expression<Func<Product, GetProductsByIdResponse>> expression = e => new GetProductsByIdResponse
            {
               // other properties
               Price = e.QuotePrice,
               PriceTaxed = e.finalQuantity,
               // other properties
            };
            var recordFilterSpec = new ProductsFilterSpecification(query.Id);
    
            var data = await _unitOfWork.Repository<Product>().Entities
                .Specify(recordFilterSpec)
                .Select(expression)
                .FirstOrDefaultAsync(cancellationToken: cancellationToken);
    
            return await Result<GetProductsByIdResponse>.SuccessAsync(data);
  3. See error

    fail: 10/8/2021 21:20:56.605 CoreEventId.QueryIterationFailed[10100] (Microsoft.EntityFrameworkCore.Query)
      An exception occurred while iterating over the results of a query for context type 'IQApps.Infrastructure.Contexts.BlazorHeroContext'.
      Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'CreatedBy'.
      Invalid column name 'CreatedOn'.
      Invalid column name 'LastModifiedBy'.
      Invalid column name 'LastModifiedOn'.
      Invalid column name 'CreatedBy'.
      Invalid column name 'CreatedOn'.
      Invalid column name 'LastModifiedBy'.
      Invalid column name 'LastModifiedOn'.
         at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
         at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
         at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj)
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
      --- End of stack trace from previous location ---
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
         at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
      --- End of stack trace from previous location ---
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
      ClientConnectionId:29c6edc4-c2a3-4eab-a54a-c94b633521d2
      Error Number:207,State:1,Class:16
    dbug: 10/8/2021 21:20:56.611 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query)
      Generated query execution expression:
      'queryContext => new SingleQueryingEnumerable<BlazorHeroUser>(
          (RelationalQueryContext)queryContext,
          RelationalCommandCache.SelectExpression(
              Projection Mapping:
                  EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: BlazorHeroUser.Id (string) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: BlazorHeroUser.AccessFailedCount (int) Required, 1], [Property: BlazorHeroUser.ConcurrencyStamp (string) Concurrency, 2], [Property: BlazorHeroUser.CreatedBy (string), 3], [Property: BlazorHeroUser.CreatedOn (DateTime) Required, 4], [Property: BlazorHeroUser.DeletedOn (Nullable<DateTime>), 5], [Property: BlazorHeroUser.Email (string) MaxLength(256), 6], [Property: BlazorHeroUser.EmailConfirmed (bool) Required, 7], [Property: BlazorHeroUser.FirstName (string), 8], [Property: BlazorHeroUser.IsActive (bool) Required, 9], [Property: BlazorHeroUser.IsDeleted (bool) Required, 10], [Property: BlazorHeroUser.LastModifiedBy (string), 11], [Property: BlazorHeroUser.LastModifiedOn (Nullable<DateTime>), 12], [Property: BlazorHeroUser.LastName (string), 13], [Property: BlazorHeroUser.LockoutEnabled (bool) Required, 14], [Property: BlazorHeroUser.LockoutEnd (Nullable<DateTimeOffset>), 15], [Property: BlazorHeroUser.NormalizedEmail (string) Index MaxLength(256), 16], [Property: BlazorHeroUser.NormalizedUserName (string) Index MaxLength(256), 17], [Property: BlazorHeroUser.PasswordHash (string), 18], [Property: BlazorHeroUser.PhoneNumber (string), 19], [Property: BlazorHeroUser.PhoneNumberConfirmed (bool) Required, 20], [Property: BlazorHeroUser.ProfilePictureDataUrl (string), 21], [Property: BlazorHeroUser.RefreshToken (string), 22], [Property: BlazorHeroUser.RefreshTokenExpiryTime (DateTime) Required, 23], [Property: BlazorHeroUser.SecurityStamp (string), 24], [Property: BlazorHeroUser.TwoFactorEnabled (bool) Required, 25], [Property: BlazorHeroUser.UserName (string) MaxLength(256), 26], }
              SELECT u.Id, u.AccessFailedCount, u.ConcurrencyStamp, u.CreatedBy, u.CreatedOn, u.DeletedOn, u.Email, u.EmailConfirmed, u.FirstName, u.IsActive, u.IsDeleted, u.LastModifiedBy, u.LastModifiedOn, u.LastName, u.LockoutEnabled, u.LockoutEnd, u.NormalizedEmail, u.NormalizedUserName, u.PasswordHash, u.PhoneNumber, u.PhoneNumberConfirmed, u.ProfilePictureDataUrl, u.RefreshToken, u.RefreshTokenExpiryTime, u.SecurityStamp, u.TwoFactorEnabled, u.UserName
              FROM Identity.Users AS u),
          Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, BlazorHeroUser>,
          IQApps.Infrastructure.Contexts.BlazorHeroContext,
          False,
          True
      )'

Expected behavior You should be able to map to a property on your domain

dotnetshadow commented 2 years ago

Closing this while I investigate more

UPDATE Found my issue, I was trying to do .Include() on a domain object that didn't have the audit fields in my database.