ASP.NET Core (二)数据库连接


一、搭建数据库连接

(1)初始化DbContext,默认SQL Server、不一致请下载对应NuGet包及修改startup链接

标准数据库上下文的定义,EntityDBContext继承DBContext,下附源码链接

 

public class EntityDBContext : DbContext
{
  public EntityDBContext(DbContextOptions<EntityDBContext> options):base(options)
  {

  }
}

 

EntityFrameworkCore DbContext源码

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore { /// <summary>
    /// A DbContext instance represents a session with the database and can be used to query and save /// instances of your entities. DbContext is a combination of the Unit Of Work and Repository patterns. /// </summary>
    /// <remarks>
    ///     <para>
    /// Typically you create a class that derives from DbContext and contains <see cref="DbSet{TEntity}" />
    /// properties for each entity in the model. If the <see cref="DbSet{TEntity}" /> properties have a public setter, /// they are automatically initialized when the instance of the derived context is created. ///     </para>
    ///     <para>
    /// Override the <see cref="OnConfiguring(DbContextOptionsBuilder)" /> method to configure the database (and /// other options) to be used for the context. Alternatively, if you would rather perform configuration externally /// instead of inline in your context, you can use <see cref="DbContextOptionsBuilder{TContext}" />
    /// (or <see cref="DbContextOptionsBuilder" />) to externally create an instance of <see cref="DbContextOptions{TContext}" />
    /// (or <see cref="DbContextOptions" />) and pass it to a base constructor of <see cref="DbContext" />. ///     </para>
    ///     <para>
    /// The model is discovered by running a set of conventions over the entity classes found in the ///         <see cref="DbSet{TEntity}" /> properties on the derived context. To further configure the model that /// is discovered by convention, you can override the <see cref="OnModelCreating(ModelBuilder)" /> method. ///     </para>
    /// </remarks>
    public class DbContext : IDisposable, IInfrastructure<IServiceProvider>, IDbContextDependencies, IDbSetCache, IDbQueryCache, IDbContextPoolable { private readonly IDictionary<Type, object> _sets = new Dictionary<Type, object>(); private readonly DbContextOptions _options; private IDbContextServices _contextServices; private IDbContextDependencies _dbContextDependencies; private DatabaseFacade _database; private ChangeTracker _changeTracker; private IServiceScope _serviceScope; private IDbContextPool _dbContextPool; private bool _initializing; private bool _disposed; /// <summary>
        ///     <para>
        /// Initializes a new instance of the <see cref="DbContext" /> class. The ///         <see cref="OnConfiguring(DbContextOptionsBuilder)" />
        /// method will be called to configure the database (and other options) to be used for this context. ///     </para>
        /// </summary>
        protected DbContext() : this(new DbContextOptions<DbContext>()) { } /// <summary>
        ///     <para>
        /// Initializes a new instance of the <see cref="DbContext" /> class using the specified options. /// The <see cref="OnConfiguring(DbContextOptionsBuilder)" /> method will still be called to allow further /// configuration of the options. ///     </para>
        /// </summary>
        /// <param name="options">The options for this context.</param>
        public DbContext([NotNull] DbContextOptions options) { Check.NotNull(options, nameof(options)); if (!options.ContextType.GetTypeInfo().IsAssignableFrom(GetType().GetTypeInfo())) { throw new InvalidOperationException(CoreStrings.NonGenericOptions(GetType().ShortDisplayName())); } _options = options; // This service is not stored in _setInitializer as this may not be the service provider that will be used // as the internal service provider going forward, because at this time OnConfiguring has not yet been called. // Mostly that isn't a problem because set initialization is done by our internal services, but in the case // where some of those services are replaced, this could initialize set using non-replaced services. // In this rare case if this is a problem for the app, then the app can just not use this mechanism to create // DbSet instances, and this code becomes a no-op. However, if this set initializer is then saved and used later // for the Set method, then it makes the problem bigger because now an app is using the non-replaced services // even when it doesn't need to.
            ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: false) .GetRequiredService<IDbSetInitializer>() .InitializeSets(this); } /// <summary>
        /// Provides access to database related information and operations for this context. /// </summary>
        public virtual DatabaseFacade Database { get { CheckDisposed(); return _database ?? (_database = new DatabaseFacade(this)); } } /// <summary>
        /// Provides access to information and operations for entity instances this context is tracking. /// </summary>
        public virtual ChangeTracker ChangeTracker => _changeTracker ?? (_changeTracker = InternalServiceProvider.GetRequiredService<IChangeTrackerFactory>().Create()); /// <summary>
        /// The metadata about the shape of entities, the relationships between them, and how they map to the database. /// </summary>
        public virtual IModel Model => DbContextDependencies.Model; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IDbSetSource IDbContextDependencies.SetSource => DbContextDependencies.SetSource; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IDbQuerySource IDbContextDependencies.QuerySource => DbContextDependencies.QuerySource; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IEntityFinderFactory IDbContextDependencies.EntityFinderFactory => DbContextDependencies.EntityFinderFactory; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IAsyncQueryProvider IDbContextDependencies.QueryProvider => DbContextDependencies.QueryProvider; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IStateManager IDbContextDependencies.StateManager => DbContextDependencies.StateManager; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IChangeDetector IDbContextDependencies.ChangeDetector => DbContextDependencies.ChangeDetector; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IEntityGraphAttacher IDbContextDependencies.EntityGraphAttacher => DbContextDependencies.EntityGraphAttacher; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IDiagnosticsLogger<DbLoggerCategory.Update> IDbContextDependencies.UpdateLogger => DbContextDependencies.UpdateLogger; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        IDiagnosticsLogger<DbLoggerCategory.Infrastructure> IDbContextDependencies.InfrastructureLogger => DbContextDependencies.InfrastructureLogger; /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        object IDbSetCache.GetOrAddSet(IDbSetSource source, Type type) { CheckDisposed(); if (!_sets.TryGetValue(type, out var set)) { set = source.Create(this, type); _sets[type] = set; } return set; } /// <summary>
        /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary>
        object IDbQueryCache.GetOrAddQuery(IDbQuerySource source, Type type) { CheckDisposed(); if (!_sets.TryGetValue(type, out var set)) { set = source.CreateQuery(this, type); _sets[type] = set; } return set; } /// <summary>
        /// Creates a <see cref="DbSet{TEntity}" /> that can be used to query and save instances of <typeparamref name="TEntity" />. /// </summary>
        /// <typeparam name="TEntity"> The type of entity for which a set should be returned. </typeparam>
        /// <returns> A set for the given entity type. </returns>
        public virtual DbSet<TEntity> Set<TEntity>() where TEntity : class
            => (DbSet<TEntity>)((IDbSetCache)this).GetOrAddSet(DbContextDependencies.SetSource, typeof(TEntity)); /// <summary>
        /// Creates a <see cref="DbQuery{TQuery}" /> that can be used to query instances of <typeparamref name="TQuery" />. /// </summary>
        /// <typeparam name="TQuery"> The type of query for which a DbQuery should be returned. </typeparam>
        /// <returns> A DbQuery for the given query type. </returns>
        public virtual DbQuery<TQuery> Query<TQuery>() where TQuery : class
            => (DbQuery<TQuery>)((IDbQueryCache)this).GetOrAddQuery(DbContextDependencies.QuerySource, typeof(TQuery)); private IEntityFinder Finder(Type type) { var entityType = Model.FindEntityType(type); if (entityType == null) { if (Model.HasEntityTypeWithDefiningNavigation(type)) { throw new InvalidOperationException(CoreStrings.InvalidSetTypeWeak(type.ShortDisplayName())); } throw new InvalidOperationException(CoreStrings.InvalidSetType(type.ShortDisplayName())); } if (entityType.IsQueryType) { throw new InvalidOperationException(CoreStrings.InvalidSetTypeQuery(type.ShortDisplayName())); } return DbContextDependencies.EntityFinderFactory.Create(entityType); } private IServiceProvider InternalServiceProvider { get { CheckDisposed(); if (_contextServices != null) { return _contextServices.InternalServiceProvider; } if (_initializing) { throw new InvalidOperationException(CoreStrings.RecursiveOnConfiguring); } try { _initializing = true; var optionsBuilder = new DbContextOptionsBuilder(_options); OnConfiguring(optionsBuilder); if (_options.IsFrozen && !ReferenceEquals(_options, optionsBuilder.Options)) { throw new InvalidOperationException(CoreStrings.PoolingOptionsModified); } var options = optionsBuilder.Options; _serviceScope = ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: true) .GetRequiredService<IServiceScopeFactory>() .CreateScope(); var scopedServiceProvider = _serviceScope.ServiceProvider; var contextServices = scopedServiceProvider.GetService<IDbContextServices>(); contextServices.Initialize(scopedServiceProvider, options, this); _contextServices = contextServices; DbContextDependencies.InfrastructureLogger.ContextInitialized(this, options); } finally { _initializing = false; } return _contextServices.InternalServiceProvider; } } private IDbContextDependencies DbContextDependencies { get { CheckDisposed(); return _dbContextDependencies ?? (_dbContextDependencies = InternalServiceProvider.GetRequiredService<IDbContextDependencies>()); } } [DebuggerStepThrough] private void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().ShortDisplayName(), CoreStrings.ContextDisposed); } } /// <summary>
        ///     <para>
        /// Override this method to configure the database (and other options) to be used for this context. /// This method is called for each instance of the context that is created. /// The base implementation does nothing. ///     </para>
        ///     <para>
        /// In situations where an instance of <see cref="DbContextOptions" /> may or may not have been passed /// to the constructor, you can use <see cref="DbContextOptionsBuilder.IsConfigured" /> to determine if /// the options have already been set, and skip some or all of the logic in ///         <see cref="OnConfiguring(DbContextOptionsBuilder)" />. ///     </para>
        /// </summary>
        /// <param name="optionsBuilder">
        /// A builder used to create or modify options for this context. Databases (and other extensions) /// typically define extension methods on this object that allow you to configure the context. /// </param>
        protected internal virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { } /// <summary>
        /// Override this method to further configure the model that was discovered by convention from the entity types /// exposed in <see cref="DbSet{TEntity}" /> properties on your derived context. The resulting model may be cached /// and re-used for subsequent instances of your derived context. /// </summary>
        /// <remarks>
        /// If a model is explicitly set on the options for this context (via <see cref="DbContextOptionsBuilder.UseModel(IModel)" />) /// then this method will not be run. /// </remarks>
        /// <param name="modelBuilder">
        /// The builder being used to construct the model for this context. Databases (and other extensions) typically /// define extension methods on this object that allow you to configure aspects of the model that are specific /// to a given database. /// </param>
        protected internal virtual void OnModelCreating(ModelBuilder modelBuilder) { } /// <summary>
        /// Saves all changes made in this context to the database. /// </summary>
        /// <remarks>
        /// This method will automatically call <see cref="ChangeTracking.ChangeTracker.DetectChanges" /> to discover any /// changes to entity instances before saving to the underlying database. This can be disabled via ///     <see cref="ChangeTracking.ChangeTracker.AutoDetectChangesEnabled" />. /// </remarks>
        /// <returns>
        /// The number of state entries written to the database. /// </returns>
        /// <exception cref="DbUpdateException">
        /// An error is encountered while saving to the database. /// </exception>
        /// <exception cref="DbUpdateConcurrencyException">
        /// A concurrency violation is encountered while saving to the database. /// A concurrency violation occurs when an unexpected number of rows are affected during save. /// This is usually because the data in the database has been modified since it was loaded into memory. /// </exception>
 [DebuggerStepThrough] public virtual int SaveChanges() => SaveChanges(acceptAllChangesOnSuccess: true); /// <summary>
        /// Saves all changes made in this context to the database. /// </summary>
        /// <param name="acceptAllChangesOnSuccess">
        /// Indicates whether <see cref="ChangeTracking.ChangeTracker.AcceptAllChanges" /> is called after the changes have /// been sent successfully to the database. /// </param>
        /// <remarks>
        /// This method will automatically call <see cref="ChangeTracking.ChangeTracker.DetectChanges" /> to discover any /// changes to entity instances before saving to the underlying database. This can be disabled via ///     <see cref="ChangeTracking.ChangeTracker.AutoDetectChangesEnabled" />. /// </remarks>
        /// <returns>
        /// The number of state entries written to the database. /// </returns>
        /// <exception cref="DbUpdateException">
        /// An error is encountered while saving to the database. /// </exception>
        /// <exception cref="DbUpdateConcurrencyException">
        /// A concurrency violation is encountered while saving to the database. /// A concurrency violation occurs when an unexpected number of rows are affected during save. /// This is usually because the data in the database has been modified since it was loaded into memory. /// </exception>
 [DebuggerStepThrough] public virtual int SaveChanges(bool acceptAllChangesOnSuccess) { CheckDisposed(); DbContextDependencies.UpdateLogger.SaveChangesStarting(this); TryDetectChanges(); try { var entitiesSaved = DbContextDependencies.StateManager.SaveChanges(acceptAllChangesOnSuccess); DbContextDependencies.UpdateLogger.SaveChangesCompleted(this, entitiesSaved); return entitiesSaved; } catch (Exception exception) { DbContextDependencies.UpdateLogger.SaveChangesFailed(this, exception); throw; } } private void TryDetectChanges() { if (ChangeTracker.AutoDetectChangesEnabled) { ChangeTracker.DetectChanges(); } } /// <summary>
        /// Asynchronously saves all changes made in this context to the database. /// </summary>
        /// <remarks>
        ///     <para>
        /// This method will automatically call <see cref="ChangeTracking.ChangeTracker.DetectChanges" /> to discover any /// changes to entity instances before saving to the underlying database. This can be disabled via ///         <see cref="ChangeTracking.ChangeTracker.AutoDetectChangesEnabled" />. ///     </para>
        ///     <para>
        /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure /// that any asynchronous operations have completed before calling another method on this context. ///     </para>
        /// </remarks>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>
        /// A task that represents the asynchronous save operation. The task result contains the /// number of state entries written to the database. /// </returns>
        /// <exception cref="DbUpdateException">
        /// An error is encountered while saving to the database. /// </exception>
        /// <exception cref="DbUpdateConcurrencyException">
        /// A concurrency violation is encountered while saving to the database. /// A concurrency violation occurs when an unexpected number of rows are affected during save. /// This is usually because the data in the database has been modified since it was loaded into memory. /// </exception>
        public virtual Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) => SaveChangesAsync(acceptAllChangesOnSuccess: true, cancellationToken: cancellationToken); /// <summary>
        /// Asynchronously saves all changes made in this context to the database. /// </summary>
        /// <param name="acceptAllChangesOnSuccess">
        /// Indicates whether <see cref="ChangeTracking.ChangeTracker.AcceptAllChanges" /> is called after the changes have /// been sent successfully to the database. /// </param>
        /// <remarks>
        ///     <para>
        /// This method will automatically call <see cref="ChangeTracking.ChangeTracker.DetectChanges" /> to discover any /// changes to entity instances before saving to the underlying database. This can be disabled via ///         <see cref="ChangeTracking.ChangeTracker.AutoDetectChangesEnabled" />. ///     </para>
        ///     <para>
        /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure /// that any asynchronous operations have completed before calling another method on this context. ///     </para>
        /// </remarks>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>
        /// A task that represents the asynchronous save operation. The task result contains the /// number of state entries written to the database. /// </returns>
        /// <exception cref="DbUpdateException">
        /// An error is encountered while saving to the database. /// </exception>
        /// <exception cref="DbUpdateConcurrencyException">
        /// A concurrency violation is encountered while saving to the database. /// A concurrency violation occurs when an unexpected number of rows are affected during save. /// This is usually because the data in the database has been modified since it was loaded into memory. /// </exception>
        public virtual async Task<int> SaveChangesAsync( bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { CheckDisposed(); DbContextDependencies.UpdateLogger.SaveChangesStarting(this); TryDetectChanges(); try { var entitiesSaved = await DbContextDependencies.StateManager.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); DbContextDependencies.UpdateLogger.SaveChangesCompleted(this, entitiesSaved); return entitiesSaved; } catch (Exception exception) { DbContextDependencies.UpdateLogger.SaveChangesFailed(this, exception); throw; } } void IDbContextPoolable.SetPool(IDbContextPool contextPool) { _dbContextPool = contextPool; } DbContextPoolConfigurationSnapshot IDbContextPoolable.SnapshotConfiguration() => new DbContextPoolConfigurationSnapshot( _changeTracker?.AutoDetectChangesEnabled, _changeTracker?.QueryTrackingBehavior, _database?.AutoTransactionsEnabled); void IDbContextPoolable.Resurrect(DbContextPoolConfigurationSnapshot configurationSnapshot) { _disposed = false; if (configurationSnapshot.AutoDetectChangesEnabled != null) { ChangeTracker.AutoDetectChangesEnabled = configurationSnapshot.AutoDetectChangesEnabled.Value; } if (configurationSnapshot.QueryTrackingBehavior != null) { ChangeTracker.QueryTrackingBehavior = configurationSnapshot.QueryTrackingBehavior.Value; } if (configurationSnapshot.AutoTransactionsEnabled != null) { Database.AutoTransactionsEnabled = configurationSnapshot.AutoTransactionsEnabled.Value; } } void IDbContextPoolable.ResetState() { var resettableServices = _contextServices?.InternalServiceProvider? .GetService<IEnumerable<IResettableService>>()?.ToList(); if (resettableServices != null) { foreach (var service in resettableServices) { service.ResetState(); } } _disposed = true; } /// <summary>
        /// Releases the allocated resources for this context. /// </summary>
        public virtual void Dispose() { if (_dbContextPool == null
                && !_disposed) { _dbContextDependencies?.InfrastructureLogger.ContextDisposed(this); _disposed = true; _dbContextDependencies?.StateManager.Unsubscribe(); _serviceScope?.Dispose(); _dbContextDependencies = null; _changeTracker = null; _database = null; } } /// <summary>
        /// Gets an <see cref="EntityEntry{TEntity}" /> for the given entity. The entry provides /// access to change tracking information and operations for the entity. /// </summary>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to get the entry for. </param>
        /// <returns> The entry for the given entity. </returns>
        public virtual EntityEntry<TEntity> Entry<TEntity>([NotNull] TEntity entity) where TEntity : class { Check.NotNull(entity, nameof(entity)); CheckDisposed(); TryDetectChanges(); return EntryWithoutDetectChanges(entity); } private EntityEntry<TEntity> EntryWithoutDetectChanges<TEntity>(TEntity entity) where TEntity : class
            => new EntityEntry<TEntity>(DbContextDependencies.StateManager.GetOrCreateEntry(entity)); /// <summary>
        ///     <para>
        /// Gets an <see cref="EntityEntry" /> for the given entity. The entry provides /// access to change tracking information and operations for the entity. ///     </para>
        ///     <para>
        /// This method may be called on an entity that is not tracked. You can then /// set the <see cref="EntityEntry.State" /> property on the returned entry /// to have the context begin tracking the entity in the specified state. ///     </para>
        /// </summary>
        /// <param name="entity"> The entity to get the entry for. </param>
        /// <returns> The entry for the given entity. </returns>
        public virtual EntityEntry Entry([NotNull] object entity) { Check.NotNull(entity, nameof(entity)); CheckDisposed(); TryDetectChanges(); return EntryWithoutDetectChanges(entity); } private EntityEntry EntryWithoutDetectChanges(object entity) => new EntityEntry(DbContextDependencies.StateManager.GetOrCreateEntry(entity)); private void SetEntityState(InternalEntityEntry entry, EntityState entityState) { if (entry.EntityState == EntityState.Detached) { DbContextDependencies.EntityGraphAttacher.AttachGraph(entry, entityState, forceStateWhenUnknownKey: true); } else { entry.SetEntityState( entityState, acceptChanges: true, forceStateWhenUnknownKey: true); } } private async Task SetEntityStateAsync( InternalEntityEntry entry, EntityState entityState, CancellationToken cancellationToken) { if (entry.EntityState == EntityState.Detached) { await DbContextDependencies.EntityGraphAttacher.AttachGraphAsync( entry, entityState, forceStateWhenUnknownKey: true, cancellationToken: cancellationToken); } else { await entry.SetEntityStateAsync( entityState, acceptChanges: true, forceStateWhenUnknownKey: true, cancellationToken: cancellationToken); } } /// <summary>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that /// they will be inserted into the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to add. </param>
        /// <returns>
        /// The <see cref="EntityEntry{TEntity}" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry<TEntity> Add<TEntity>([NotNull] TEntity entity) where TEntity : class { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Added); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// This method is async only to allow special value generators, such as the one used by /// 'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo', /// to access the database asynchronously. For all other cases the non async method should be used. ///     </para>
        /// </summary>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to add. </param>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>
        /// A task that represents the asynchronous Add operation. The task result contains the ///     <see cref="EntityEntry{TEntity}" /> for the entity. The entry provides access to change tracking /// information and operations for the entity. /// </returns>
        public virtual async Task<EntityEntry<TEntity>> AddAsync<TEntity>( [NotNull] TEntity entity, CancellationToken cancellationToken = default) where TEntity : class { CheckDisposed(); var entry = EntryWithoutDetectChanges(Check.NotNull(entity, nameof(entity))); await SetEntityStateAsync(entry.GetInfrastructure(), EntityState.Added, cancellationToken); return entry; } /// <summary>
        ///     <para>
        /// Begins tracking the given entity in the <see cref="EntityState.Unchanged" /> state /// such that no operation will be performed when <see cref="DbContext.SaveChanges()" />
        /// is called. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Unchanged" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to attach. </param>
        /// <returns>
        /// The <see cref="EntityEntry{TEntity}" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry<TEntity> Attach<TEntity>([NotNull] TEntity entity) where TEntity : class { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Unchanged); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity in the <see cref="EntityState.Modified" /> state such that it will /// be updated in the database when <see cref="DbContext.SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// All properties of the entity will be marked as modified. To mark only some properties as modified, use ///         <see cref="Attach{TEntity}(TEntity)" /> to begin tracking the entity in the <see cref="EntityState.Unchanged" />
        /// state and then use the returned <see cref="EntityEntry" /> to mark the desired properties as modified. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Modified" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to update. </param>
        /// <returns>
        /// The <see cref="EntityEntry{TEntity}" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry<TEntity> Update<TEntity>([NotNull] TEntity entity) where TEntity : class { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Modified); } /// <summary>
        /// Begins tracking the given entity in the <see cref="EntityState.Deleted" /> state such that it will /// be removed from the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <remarks>
        ///     <para>
        /// If the entity is already tracked in the <see cref="EntityState.Added" /> state then the context will /// stop tracking the entity (rather than marking it as <see cref="EntityState.Deleted" />) since the /// entity was previously added to the context and does not exist in the database. ///     </para>
        ///     <para>
        /// Any other reachable entities that are not already being tracked will be tracked in the same way that /// they would be if <see cref="Attach{TEntity}(TEntity)" /> was called before calling this method. /// This allows any cascading actions to be applied when <see cref="SaveChanges()" /> is called. ///     </para>
        /// </remarks>
        /// <typeparam name="TEntity"> The type of the entity. </typeparam>
        /// <param name="entity"> The entity to remove. </param>
        /// <returns>
        /// The <see cref="EntityEntry{TEntity}" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry<TEntity> Remove<TEntity>([NotNull] TEntity entity) where TEntity : class { Check.NotNull(entity, nameof(entity)); CheckDisposed(); var entry = EntryWithoutDetectChanges(entity); var initialState = entry.State; if (initialState == EntityState.Detached) { SetEntityState(entry.GetInfrastructure(), EntityState.Unchanged); } // An Added entity does not yet exist in the database. If it is then marked as deleted there is // nothing to delete because it was not yet inserted, so just make sure it doesn't get inserted.
            entry.State = initialState == EntityState.Added ? EntityState.Detached : EntityState.Deleted; return entry; } private EntityEntry<TEntity> SetEntityState<TEntity>( TEntity entity, EntityState entityState) where TEntity : class { var entry = EntryWithoutDetectChanges(entity); SetEntityState(entry.GetInfrastructure(), entityState); return entry; } /// <summary>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <param name="entity"> The entity to add. </param>
        /// <returns>
        /// The <see cref="EntityEntry" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry Add([NotNull] object entity) { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Added); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// This method is async only to allow special value generators, such as the one used by /// 'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo', /// to access the database asynchronously. For all other cases the non async method should be used. ///     </para>
        /// </summary>
        /// <param name="entity"> The entity to add. </param>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>
        /// A task that represents the asynchronous Add operation. The task result contains the ///     <see cref="EntityEntry" /> for the entity. The entry provides access to change tracking /// information and operations for the entity. /// </returns>
        public virtual async Task<EntityEntry> AddAsync( [NotNull] object entity, CancellationToken cancellationToken = default) { CheckDisposed(); var entry = EntryWithoutDetectChanges(Check.NotNull(entity, nameof(entity))); await SetEntityStateAsync(entry.GetInfrastructure(), EntityState.Added, cancellationToken); return entry; } /// <summary>
        ///     <para>
        /// Begins tracking the given entity in the <see cref="EntityState.Unchanged" /> state /// such that no operation will be performed when <see cref="DbContext.SaveChanges()" />
        /// is called. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Unchanged" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entity"> The entity to attach. </param>
        /// <returns>
        /// The <see cref="EntityEntry" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry Attach([NotNull] object entity) { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Unchanged); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity in the <see cref="EntityState.Modified" /> state such that it will /// be updated in the database when <see cref="DbContext.SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// All properties of the entity will be marked as modified. To mark only some properties as modified, use ///         <see cref="Attach(object)" /> to begin tracking the entity in the <see cref="EntityState.Unchanged" />
        /// state and then use the returned <see cref="EntityEntry" /> to mark the desired properties as modified. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Modified" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entity"> The entity to update. </param>
        /// <returns>
        /// The <see cref="EntityEntry" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry Update([NotNull] object entity) { CheckDisposed(); return SetEntityState(Check.NotNull(entity, nameof(entity)), EntityState.Modified); } /// <summary>
        /// Begins tracking the given entity in the <see cref="EntityState.Deleted" /> state such that it will /// be removed from the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <remarks>
        ///     <para>
        /// If the entity is already tracked in the <see cref="EntityState.Added" /> state then the context will /// stop tracking the entity (rather than marking it as <see cref="EntityState.Deleted" />) since the /// entity was previously added to the context and does not exist in the database. ///     </para>
        ///     <para>
        /// Any other reachable entities that are not already being tracked will be tracked in the same way that /// they would be if <see cref="Attach(object)" /> was called before calling this method. /// This allows any cascading actions to be applied when <see cref="SaveChanges()" /> is called. ///     </para>
        /// </remarks>
        /// <param name="entity"> The entity to remove. </param>
        /// <returns>
        /// The <see cref="EntityEntry" /> for the entity. The entry provides /// access to change tracking information and operations for the entity. /// </returns>
        public virtual EntityEntry Remove([NotNull] object entity) { Check.NotNull(entity, nameof(entity)); CheckDisposed(); var entry = EntryWithoutDetectChanges(entity); var initialState = entry.State; if (initialState == EntityState.Detached) { SetEntityState(entry.GetInfrastructure(), EntityState.Unchanged); } // An Added entity does not yet exist in the database. If it is then marked as deleted there is // nothing to delete because it was not yet inserted, so just make sure it doesn't get inserted.
            entry.State = initialState == EntityState.Added ? EntityState.Detached : EntityState.Deleted; return entry; } private EntityEntry SetEntityState(object entity, EntityState entityState) { var entry = EntryWithoutDetectChanges(entity); SetEntityState(entry.GetInfrastructure(), entityState); return entry; } /// <summary>
        /// Begins tracking the given entities, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <param name="entities"> The entities to add. </param>
        public virtual void AddRange([NotNull] params object[] entities) { CheckDisposed(); AddRange((IEnumerable<object>)entities); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// This method is async only to allow special value generators, such as the one used by /// 'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo', /// to access the database asynchronously. For all other cases the non async method should be used. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to add. </param>
        /// <returns> A task that represents the asynchronous operation. </returns>
        public virtual Task AddRangeAsync([NotNull] params object[] entities) { CheckDisposed(); return AddRangeAsync((IEnumerable<object>)entities); } /// <summary>
        ///     <para>
        /// Begins tracking the given entities in the <see cref="EntityState.Unchanged" /> state /// such that no operation will be performed when <see cref="DbContext.SaveChanges()" />
        /// is called. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Unchanged" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to attach. </param>
        public virtual void AttachRange([NotNull] params object[] entities) { CheckDisposed(); AttachRange((IEnumerable<object>)entities); } /// <summary>
        ///     <para>
        /// Begins tracking the given entities in the <see cref="EntityState.Modified" /> state such that they will /// be updated in the database when <see cref="DbContext.SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// All properties of each entity will be marked as modified. To mark only some properties as modified, use ///         <see cref="Attach(object)" /> to begin tracking each entity in the <see cref="EntityState.Unchanged" />
        /// state and then use the returned <see cref="EntityEntry" /> to mark the desired properties as modified. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Modified" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to update. </param>
        public virtual void UpdateRange([NotNull] params object[] entities) { CheckDisposed(); UpdateRange((IEnumerable<object>)entities); } /// <summary>
        /// Begins tracking the given entity in the <see cref="EntityState.Deleted" /> state such that it will /// be removed from the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <remarks>
        ///     <para>
        /// If any of the entities are already tracked in the <see cref="EntityState.Added" /> state then the context will /// stop tracking those entities (rather than marking them as <see cref="EntityState.Deleted" />) since those /// entities were previously added to the context and do not exist in the database. ///     </para>
        ///     <para>
        /// Any other reachable entities that are not already being tracked will be tracked in the same way that /// they would be if <see cref="AttachRange(object[])" /> was called before calling this method. /// This allows any cascading actions to be applied when <see cref="SaveChanges()" /> is called. ///     </para>
        /// </remarks>
        /// <param name="entities"> The entities to remove. </param>
        public virtual void RemoveRange([NotNull] params object[] entities) { CheckDisposed(); RemoveRange((IEnumerable<object>)entities); } private void SetEntityStates(IEnumerable<object> entities, EntityState entityState) { var stateManager = DbContextDependencies.StateManager; foreach (var entity in entities) { SetEntityState(stateManager.GetOrCreateEntry(entity), entityState); } } /// <summary>
        /// Begins tracking the given entities, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <param name="entities"> The entities to add. </param>
        public virtual void AddRange([NotNull] IEnumerable<object> entities) { CheckDisposed(); SetEntityStates(Check.NotNull(entities, nameof(entities)), EntityState.Added); } /// <summary>
        ///     <para>
        /// Begins tracking the given entity, and any other reachable entities that are /// not already being tracked, in the <see cref="EntityState.Added" /> state such that they will /// be inserted into the database when <see cref="SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// This method is async only to allow special value generators, such as the one used by /// 'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo', /// to access the database asynchronously. For all other cases the non async method should be used. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to add. </param>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>
        /// A task that represents the asynchronous operation. /// </returns>
        public virtual async Task AddRangeAsync( [NotNull] IEnumerable<object> entities, CancellationToken cancellationToken = default) { CheckDisposed(); var stateManager = DbContextDependencies.StateManager; foreach (var entity in entities) { await SetEntityStateAsync( stateManager.GetOrCreateEntry(entity), EntityState.Added, cancellationToken); } } /// <summary>
        ///     <para>
        /// Begins tracking the given entities in the <see cref="EntityState.Unchanged" /> state /// such that no operation will be performed when <see cref="DbContext.SaveChanges()" />
        /// is called. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Unchanged" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to attach. </param>
        public virtual void AttachRange([NotNull] IEnumerable<object> entities) { CheckDisposed(); SetEntityStates(Check.NotNull(entities, nameof(entities)), EntityState.Unchanged); } /// <summary>
        ///     <para>
        /// Begins tracking the given entities in the <see cref="EntityState.Modified" /> state such that they will /// be updated in the database when <see cref="DbContext.SaveChanges()" /> is called. ///     </para>
        ///     <para>
        /// All properties of each entity will be marked as modified. To mark only some properties as modified, use ///         <see cref="Attach(object)" /> to begin tracking each entity in the <see cref="EntityState.Unchanged" />
        /// state and then use the returned <see cref="EntityEntry" /> to mark the desired properties as modified. ///     </para>
        ///     <para>
        /// A recursive search of the navigation properties will be performed to find reachable entities /// that are not already being tracked by the context. These entities will also begin to be tracked /// by the context. If a reachable entity has its primary key value set /// then it will be tracked in the <see cref="EntityState.Modified" /> state. If the primary key /// value is not set then it will be tracked in the <see cref="EntityState.Added" /> state. /// An entity is considered to have its primary key value set if the primary key property is set /// to anything other than the CLR default for the property type. ///     </para>
        /// </summary>
        /// <param name="entities"> The entities to update. </param>
        public virtual void UpdateRange([NotNull] IEnumerable<object> entities) { CheckDisposed(); SetEntityStates(Check.NotNull(entities, nameof(entities)), EntityState.Modified); } /// <summary>
        /// Begins tracking the given entity in the <see cref="EntityState.Deleted" /> state such that it will /// be removed from the database when <see cref="SaveChanges()" /> is called. /// </summary>
        /// <remarks>
        ///     <para>
        /// If any of the entities are already tracked in the <see cref="EntityState.Added" /> state then the context will /// stop tracking those entities (rather than marking them as <see cref="EntityState.Deleted" />) since those /// entities were previously added to the context and do not exist in the database. ///     </para>
        ///     <para>
        /// Any other reachable entities that are not already being tracked will be tracked in the same way that /// they would be if <see cref="AttachRange(IEnumerable{object})" /> was called before calling this method. /// This allows any cascading actions to be applied when <see cref="SaveChanges()" /> is called. ///     </para>
        /// </remarks>
        /// <param name="entities"> The entities to remove. </param>
        public virtual void RemoveRange([NotNull] IEnumerable<object> entities) { Check.NotNull(entities, nameof(entities)); CheckDisposed(); var stateManager = DbContextDependencies.StateManager; // An Added entity does not yet exist in the database. If it is then marked as deleted there is // nothing to delete because it was not yet inserted, so just make sure it doesn't get inserted.
            foreach (var entity in entities) { var entry = stateManager.GetOrCreateEntry(entity); var initialState = entry.EntityState; if (initialState == EntityState.Detached) { SetEntityState(entry, EntityState.Unchanged); } entry.SetEntityState( initialState == EntityState.Added ? EntityState.Detached : EntityState.Deleted); } } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <param name="entityType"> The type of entity to find. </param>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual object Find([NotNull] Type entityType, [CanBeNull] params object[] keyValues) { CheckDisposed(); return Finder(entityType).Find(keyValues); } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <param name="entityType"> The type of entity to find. </param>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual Task<object> FindAsync([NotNull] Type entityType, [CanBeNull] params object[] keyValues) { CheckDisposed(); return Finder(entityType).FindAsync(keyValues); } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <param name="entityType"> The type of entity to find. </param>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual Task<object> FindAsync([NotNull] Type entityType, [CanBeNull] object[] keyValues, CancellationToken cancellationToken) { CheckDisposed(); return Finder(entityType).FindAsync(keyValues, cancellationToken); } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <typeparam name="TEntity"> The type of entity to find. </typeparam>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual TEntity Find<TEntity>([CanBeNull] params object[] keyValues) where TEntity : class { CheckDisposed(); return ((IEntityFinder<TEntity>)Finder(typeof(TEntity))).Find(keyValues); } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <typeparam name="TEntity"> The type of entity to find. </typeparam>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual Task<TEntity> FindAsync<TEntity>([CanBeNull] params object[] keyValues) where TEntity : class { CheckDisposed(); return ((IEntityFinder<TEntity>)Finder(typeof(TEntity))).FindAsync(keyValues); } /// <summary>
        /// Finds an entity with the given primary key values. If an entity with the given primary key values /// is being tracked by the context, then it is returned immediately without making a request to the /// database. Otherwise, a query is made to the database for an entity with the given primary key values /// and this entity, if found, is attached to the context and returned. If no entity is found, then /// null is returned. /// </summary>
        /// <typeparam name="TEntity"> The type of entity to find. </typeparam>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
        /// <returns>The entity found, or null.</returns>
        public virtual Task<TEntity> FindAsync<TEntity>([CanBeNull] object[] keyValues, CancellationToken cancellationToken) where TEntity : class { CheckDisposed(); return ((IEntityFinder<TEntity>)Finder(typeof(TEntity))).FindAsync(keyValues, cancellationToken); } /// <summary>
        ///     <para>
        /// Gets the scoped <see cref="IServiceProvider" /> being used to resolve services. ///     </para>
        ///     <para>
        /// This property is intended for use by extension methods that need to make use of services /// not directly exposed in the public API surface. ///     </para>
        /// </summary>
        IServiceProvider IInfrastructure<IServiceProvider>.Instance => InternalServiceProvider; #region Hidden System.Object members

        /// <summary>
        /// Returns a string that represents the current object. /// </summary>
        /// <returns> A string that represents the current object. </returns>
 [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() => base.ToString(); /// <summary>
        /// Determines whether the specified object is equal to the current object. /// </summary>
        /// <param name="obj"> The object to compare with the current object. </param>
        /// <returns> true if the specified object is equal to the current object; otherwise, false. </returns>
 [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) => base.Equals(obj); /// <summary>
        /// Serves as the default hash function. /// </summary>
        /// <returns> A hash code for the current object. </returns>
 [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => base.GetHashCode(); #endregion } }
View Code

(2)Startup.cs注入数据库连接池、DI、生命周期

在这里我们不得不说一下ASP.NET Core DI 构造函数内置容器,由IServiceProvider接口表示,在startup.cs中ConfigureServices 方法中配置服务MVC、EF Core服务,即可直接使用构造注入

services.AddMvc();
//sql server
services.AddDbContextPool<EntityPoolDBContext>(options => options.UseSqlServer("your DB connection address"));
//mysql
//services.AddDbContextPool<
EntityPoolDBContext>(options => options.UseMySQL(sqlConnectionString));
构造函数内置容器使用:
private readonly EntityCore.EntityDBContext _dbcontext;
public HomeController(EntityCore.EntityDBContext dbcontext)
{
      _dbcontext = dbcontext;
}
// 下面即可使用
_dbcontext.实现
 
AddDbContextPool是2.0中新增属性,数据库连接池;目前存在以下BUG:当我们使用多个AddDbContextPool也即是需要使用多个数据库连接时候
services.AddDbContextPool<EntityDBContext>(options => options.UseSqlServer("your DB connection address"));
services.AddDbContextPool
<EntityPoolDBContext>(options => options.UseSqlServer("your DB connection address"));

将会出现An unhandled exception occurred while processing the request目前的解决是AddDbContextPool替换为AddDbContext,EF Core后续版本会修复

在说一下ConfigureServices 中服务的生命周期

 

//总是创建一个新的服务实例
services.AddTransient<IUserRepository, UserRepository>();
//(同一次请求),我理解为会话
services.AddScoped<IUserRepository, UserRepository>();
//始终是同一个实例对象
services.AddSingleton<IUserRepository, UserRepository>();

 

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM