KeyNotFoundException in GraphQL .net Entity Framework - c#

I am using below with two DbContexts:
https://github.com/SimonCropp/GraphQL.EntityFramework
I get this error:
GraphQL.ExecutionError: Error trying to resolve project.
System.Collections.Generic.KeyNotFoundException: The given key 'Models.Master.Project' was not present in the dictionary.
at System.Collections.Generic.Dictionary2.get_Item(TKey key)
at IncludeAppender.AddIncludes[TItem,TSource](IQueryable1 query, ResolveFieldContext1 context) in C:\\projects\\graphql-entityframework\\src\\GraphQL.EntityFramework\\IncludeAppender.cs:line 20
at
GraphQL.EntityFramework.EfGraphQLService.<>c__DisplayClass21_02.<b__0>d.MoveNext() in C:\projects\graphql-entityframework\src\GraphQL.EntityFramework\EfGraphQLService_Queryable.cs:line 80
End of stack trace from previous location where exception
was thrown
at GraphQL.Instrumentation.MiddlewareResolver.Resolve(ResolveFieldContext context)
at GraphQL.Execution.ExecutionStrategy.ExecuteNodeAsync(ExecutionContext context, ExecutionNode node)
End of inner exception stack trace
When I try to add two models of two DbContexts as following in startup.cs (in following, if I remove second line, then it works perfectly. But that I need for my second DbContext).
EfGraphQLConventions.RegisterInContainer(services, ProjectDataContextBuilder.ProjectModel); //This is creating issue as of now. wasn't issue when checked with my and your.
EfGraphQLConventions.RegisterInContainer(services, MasterDataContextBuilder.MasterModel);
DataContextBuilder classes are as follows:
static class MasterDataContextBuilder
{
static MasterDataContextBuilder()
{
using (var masterDataContext = InMemoryContextBuilder.Build<ecdiscoMasterContext>())
{
MasterModel = masterDataContext.Model;
}
}
public static IModel MasterModel;
}
static class ProjectDataContextBuilder
{
static ProjectDataContextBuilder()
{
using (var projectDataContext = InMemoryContextBuilder.Build<ecdiscoProjectContext>())
{
ProjectModel = projectDataContext.Model;
}
}
public static IModel ProjectModel;
}
Note: In error. Project is model of MasterDbContext.
Another DbContext is Project. which is separate per tenant. So that is ProjectDbContext (which doesn't have Project model).

this is fixed in version 6 https://github.com/SimonCropp/GraphQL.EntityFramework/blob/master/doco/configuration.md#multiple-dbcontexts
Some extra text to get over the silly 30 char min

Related

Error when trying to 'Seed' mock db into mock dbContext

I've been searching stack overflow for a solution to this problem but I haven't been able to find that one that fixes my error yet. I'm attempting to write unit test for an API that I developed. I created a mock db and mock context for it but when I try to 'seed' my mock context I'm getting this error.
The call is ambiguous between the following methods or properties: 'AppointmentAPI.UnitTests.DbContextExtensions.Seed(AppointmentAPI.Appt_Models.ApptSystemContext)' and 'AppointmentAPI.UnitTests.DbContextExtensions.Seed(AppointmentAPI.Appt_Models.ApptSystemContext)' [AppointmentAPI.UnitTests, AppointmentAPI.UnitTests]
Not really sure what the problem is because it was working fine the other day with no error and then when I started working on it today the error appeared. I'm fairly new to C# and especially writing unit tests for a .net API so any help is greatly appreciated. I'll post my two files below.
DbContextExtensions.cs
namespace AppointmentAPI.UnitTests
{
using System;
using AppointmentAPI.Appt_Models;
public static class DbContextExtensions
{
public static void Seed(this ApptSystemContext dbContext)
{
// add entities for dbContext instance
dbContext.AppointmentSlots.Add(new AppointmentSlots
{
SlotId = 1,
Date = Convert.ToDateTime("2020-03-31 00:00:00.000"),
Time = TimeSpan.Parse("12:00:00.0000000"),
ApptJson = "{'fname':'Billy','lname':'Joel','age':70,'caseWorker':'Donna', 'appStatus':'finished'}",
Timestamp = Convert.ToDateTime("2020-02-24 12:00:00.000")
});
dbContext.AppointmentSlots.Add(new AppointmentSlots
{
SlotId = 6,
Date = Convert.ToDateTime("2020-07-24 00:00:00.000"),
Time = TimeSpan.Parse("10:00:00.0000000"),
ApptJson = "{'fname':'Michael','lname':'Smith','age':52,'caseWorker':'Donna', 'appStatus':'finished'}",
Timestamp = Convert.ToDateTime("2020-06-25 09:34:00.000")
});
dbContext.SaveChanges();
}
}
}
DbContextMocker.cs
namespace AppointmentAPI.UnitTests
{
using Microsoft.EntityFrameworkCore;
using AppointmentAPI.Appt_Models;
public static class DbContextMocker
{
public static ApptSystemContext GetApptSystemContext(string dbName)
{
// create option for DbContext instance
var options = new DbContextOptionsBuilder<ApptSystemContext>()
.UseInMemoryDatabase(databaseName: dbName)
.Options;
// create instance of DbContext
var dbContext = new ApptSystemContext(options);
// add entities in memory
dbContext.Seed(); <-- error happens here
return dbContext;
}
}
}
This exception is usually thrown when two or more methods are overloaded with same amount of arguments but in different types
eg:
static void Seed(this ApplicationDbContext dbContext, string word1, string[] array = null);
static void Seed(this ApplicationDbContext dbContext, string word1, string word2 = null);
dbContext.Seed("test word"); // will throw the exception or will show as an syntax error.
With the above two files i dont see any methods as such. A clean and build might work hopefully.

ASP.NET: Check if run from migration

I have some code in my ConfigureServices that fails when running a migration:
dotnet ef migrations list
I'm trying to add a Certificate but it can't find the file (it works when starting the project as a whole). So is there a way to do something like this:
if (!CurrentEnvironment.IsMigration()) {
doMyStuffThatFailsInMigration()
}
That way I could keep my code as it is but just execute it when not running it in a migration.
Thanks
Just set a static flag in the Main method (which is not called by the dotnet-ef tool):
public class Program
{
public static bool IsStartedWithMain { get; private set; }
public static void Main(string[] args)
{
IsStartedWithMain = true;
...
}
}
and then check it when needed:
internal static void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
{
if (Program.IsStartedWithMain)
{
// do stuff which must not be run upon using the dotnet-ef tool
}
}
EDIT: in Dotnet 6.0 there's no separate ConfigureServices method. Everything is initialized in the Main method (can be created with dotnet new .. --use-program-main). In this case a flag can be used for skipping EF stuff:
private static bool IsStartedWithMain =>
Assembly.GetEntryAssembly() == Assembly.GetExecutingAssembly();
My current solution to detecting if a migration has not occurred:
using System.Linq;
// app is of type IApplicationBuilder
// RegisteredDBContext is the DBContext I have dependency injected
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<RegisteredDBContext>();
if (context.Database.GetPendingMigrations().Any())
{
var msg = "There are pending migrations application will not start. Make sure migrations are ran.";
throw new InvalidProgramException(msg);
// Instead of error throwing, other code could happen
}
}
This assumes that the migrations have been synced to the database already. If only EnsureDatabase has been called, then this approach does not work, because the migrations are all still pending.
There are other method options on the context.Database. GetMigrations and GetAppliedMigrations.

ObjectContext Not Disposed

I'm having an issue in Entity Framework 6 where an exception is consistently thrown. For the most part the application works perfectly fine until I try adding a user to a role via a linking table.
The error being thrown is the following:
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
The functionality will happily add the user to the role virtually but as soon as SaveChanges() is called the process falls over.
I'm aware of the how and why for the above error and after doing some research it's due to the the context not being disposed of correctly. So following on from that and looking into the DbContext setup I've realised IDisposable wasn't added to the configuration. Unfortunately, no matter what I've tried incorporating IDisposable at any point within the application still doesn't dispose of the contexts correctly.
So after spending a fair bit of time and having no luck via Google I'm wondering if any of you have a solution or are able to point me in the right direction.
The following is a cutdown version of the Data Layer classes I've implemented:
public class GenericRepository<T> : WebsiteContext, IGenericRepository<T> where T : class
{
public virtual void Commit()
{
SaveChanges();
}
public virtual void Delete(int id)
{
var record = Set<T>().Find(id);
if (record == null)
throw new Exception("Some Message");
Set<T>().Remove(record);
}
// ... ETC
}
public interface IGenericRepository<T> where T : class
{
void Commit();
// ... ETC
}
public class WebsiteContext : DbContext, IWebsiteContext
{
static WebsiteContext()
{
Database.SetInitializer<WebsiteContext>(null);
}
public WebsiteContext() : base("Name=WebsiteContext") { }
public IDbSet<User> Users { get; set; }
// ... ETC
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ... ETC
}
}
This implementation is loosely based around the following Stackoverflow question.
Entity Framework 6 Code First - Is Repository Implementation a Good One?
The following is a condensed version of the Service Layer class and method which is causing the issue.
private IGenericRepository<User> _userRepository;
private IGenericRepository<ApplicationUserSetting> _userSettingRepository;
private IGenericRepository<ApplicationRole> _roleRepository;
public UserManagementService()
{
_userRepository = new GenericRepository<User>();
_roleRepository = new GenericRepository<ApplicationRole>();
_userSettingRepository = new GenericRepository<ApplicationUserSetting>();
}
public void AssignUserRole(AssignRoleModel model)
{
var user = _userRepository.GetById(model.UserId);
if (user == null)
return;
var role = _roleRepository.GetById(model.RoleId);
if (role == null)
return;
user.Roles.Add(role);
_userRepository.Commit();
}
The issue, just like the error states, is because you have multiple instances of the type DbContext fetching your entities for you. Each fetched entity is then associated with the DbContext instance that retrieved it. If you want to persist changes to these entities it has to occur on the DbContext instance that it is associated with OR you have to attach it to the DbContext instance it is not associated with.
If you are trying to keep it simple I recommend you implement a DI framework like AutoFac. You can then have a single DbContext instance created per request and have it injected everywhere you need it. It will allow you to keep your existing structure (I am not going to comment on that as I consider that out of scope for this question), the end result would be that each injected GenericRepository instance has an injected WebsiteContext instance but the WebsiteContext instances are shared (all the same instance). The upside of that is no more error but the downside is you do have to be aware that any changes to any entities will result in those changes being persisted as soon as you execute the Save functionality.
Using multiple repositories causes the issue. Just use one repository (= one db context) and have different methods for getting the individual types.
E.g. _repository.Get(id)
It's way out of scope to point out how your current implementation could be made to work, but if you did want to use more than one context, you can despite what others have said.
If you do, you will have to detach the entity from the previous context first.

ServiceStack - Error trying to resolve Service {X} or one of its autowired dependencies

I am using servicestack and having problems with auto wiring.
Error trying to resolve Service '{Service}' or one of its autowired dependencies (see inner exception for details)
I don't need help figuring how exactly what the problem is. What I actually want is a way to see the inner exception. The inner exception should tell me the except problem without me having to figure it out but it not displayed in either the exception returned, or in the logs.
Setting DebugMode doesn't help either, it just includes the stack track of the topmost exception.
So basically, how do I stop servicestack from hiding the inner exception details?
I ran into this same issue and it ended up being that there was an exception being thrown inside of the constructor that I had created for the particular endpoint class. Example...
public class PartnerService : Service
{
private PartnerManagementService _partnerManagementService;
public PartnerService()
{
var configuration = new Configuration();
_partnerManagementService = new PartnerManagementService(configuration);
}
public object Get(PartnerGet partner)
{
try
{
var partners = _partnerManagementService.getPartners();
if (!partners.Any())
{
return new HttpError(HttpStatusCode.NotFound, "Partners Could not be found");
}
return partners;
}
catch (Exception e)
{
return new HttpError(HttpStatusCode.InternalServerError, e);
}
}
If it so happens that an exception is thrown inside of the constructor, ServiceStack will not be able to resolve the service or one of its dependencies, in this case that dependency being the constructor for the class.
If you put a try/catch in the constructor for the class you could get an exception that actually makes sense.
ServiceStack should already return the inner Exception, i.e. here's the source of the error:
private Exception CreateResolveException<TService>(Exception ex)
{
var errMsg = "Error trying to resolve Service '{0}' or one of its autowired dependencies (see inner exception for details).".Fmt(typeof(TService).FullName);
return new Exception(errMsg, ex);
}
Basically there was a problem with your IOC configuration and that one of the dependencies caused an error.
You can change ServiceStack to serialize the Inner Exception with:
SetConfig(new EndpointHostConfig {
ReturnsInnerException = true,
});
But this already defaults to true.
So the exception should already contain the Inner Exception, are you referring to what Exception gets serialized or the exception thrown in code?
One option could be to grab the actual source code from Github and add it as a project to your solution, as opposed to using a compiled DLL, then you could step through the actual code and see exactly where the exception is raised and why.
I have exactly the same exception.
In my case, it happens once migrated to ServiceStack v4. With v3, all works perfectly.
IoC configuration
public class AppHost : AppHostBase
{
public AppHost() : base("Northwind web services", typeof(CustomersService).Assembly)
{ }
public override void Configure( Container container )
{
SetConfig(new HostConfig
{
DebugMode = true,
ReturnsInnerException = true,
});
var dbFactory = new OrmLiteConnectionFactory("~/Northwind.sqlite".MapHostAbsolutePath(), SqliteDialect.Provider);
container.Register(dbFactory);
// Dependencies
container.RegisterAs<CustomerEntityRepository, ICustomerEntityRepository>();
container.RegisterAutoWired<CustomersService>();
}
}
Base class
public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : IEntity, new()
{
protected IDbConnectionFactory dbFactory { get; set; }
public Repository( IDbConnectionFactory factory )
{
dbFactory = factory;
}
}
Inherited class
public class CustomerEntityRepository : Repository<CustomerEntity>, ICustomerEntityRepository
{
public CustomerEntityRepository( IDbConnectionFactory dbFactory )
: base(dbFactory)
{
}
}
}
Only solution I've found is:
container.RegisterAs<ICustomerEntityRepository>(c => new CustomerEntityRepository(dbFactury));
Here's full exception message returned http://pastebin.com/jJntNN5p

ASP.net Entity Framework Code First custom initializer never called

I am using EF Code First in an Asp.net project. Code First created my database just fine but I am having fits getting it to seed my database with data. I am using a custom database initializer like so:
namespace Toolkit.Model
{
public class EntitiesContextInitializer : DropCreateDatabaseIfModelChanges<ToolkitContext>
{
protected override void Seed(ToolkitContext context)
{
List<Server> Servers = new List<Server>
{
new Server { ServerName = "Server 16" },
new Server { ServerName = "Server 29" }
};
foreach (Server server in Servers)
{
context.Servers.Add(server);
}
context.SaveChanges();
}
}
}
I set the initializer in the the Global.asax.cs and force the database to initialize like so:
using Toolkit.Model;
namespace Toolkit
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
Database.SetInitializer(new EntitiesContextInitializer());
var context = new ToolkitContext();
context.Database.Initialize(false);
}
...
}
The problem is that my Initializer never gets called and so my database never gets populated. I have even tried changing the initializer to inherit DropCreateDatabaseAlways instead but still nothing... What am I missing here?
EDIT
So after dropping the database my initializer got called fine. However I still wonder why it didn't get called in other instances such as when I added a table (so the model changed) or when I changed the initialzer to be DropCreateDatabaseAlways.
I'm using a similar configuration for a SQL CE database, but instead of DropCreate, I'm using CreateIfNotExists.
The difference between what I have and yours is that you have are creating a context and calling initialize on the database. Based on that, I would suggest that you remove the context setup and the Database.initialize calls that you have in the application_start method.
try
Database.SetInitializer<MyContextName>(new EntitiesContextInitializer)
in EF 5.0 the library is declared as
public static void SetInitializer<TContext>(IDatabaseInitializer<TContext> strategy) where TContext : DbContext;
You can use Database Migrations with Code-First. Whenever you use Update-database command when your model changes, your Seed method (in Migrations) will be called.
This article explains more:
http://msdn.microsoft.com/en-gb/data/jj591621
You can also force it in Application_Start() your way:
Database.SetInitializer(new EntitiesContextInitializer());
var context = new ToolkitContext();
context.Database.Initialize(true);
At the end of your overriden menthod, Seed, in class, EntitiesContextInitializer, place at the end of the method add a line:
base.Seed (context)

Categories