Skip to content

DbContext without defining DbSets are not Supported #550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ahmadalli opened this issue Aug 27, 2019 · 11 comments · Fixed by #731
Closed

DbContext without defining DbSets are not Supported #550

ahmadalli opened this issue Aug 27, 2019 · 11 comments · Fixed by #731

Comments

@ahmadalli
Copy link

ahmadalli commented Aug 27, 2019

Description

this is my DbSet:

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        Type[] types = typeof(AppDbContext).GetTypeInfo().Assembly.GetTypes();
        IEnumerable<Type> typesToRegister = types
            .Where(type => type.GetInterfaces()
                .Where(i => i.IsGenericType)
                .Any(i => i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)));

        foreach (var type in typesToRegister)
        {
            dynamic configurationInstance = Activator.CreateInstance(type);
            addConfiguration(modelBuilder, configurationInstance);
        }

        base.OnModelCreating(modelBuilder);
    }

    private static void addConfiguration<TEntity>(ModelBuilder modelBuilder, IEntityTypeConfiguration<TEntity> configuration)
        where TEntity : class
    {
        configuration.Configure(modelBuilder.Entity<TEntity>());
    }
}

and I configure each model using

#region configurations
public class Configuration : IEntityTypeConfiguration<Person>
{
    public void Configure(EntityTypeBuilder<Person> builder)
    {
        builder.ToTable(nameof(Person).Pluralize());
        builder.HasKey(x => x.Code);

        builder.Ignore(x => x.Id).Ignore(x => x.StringId);
    }
}
#endregion

inside my model.

but when I try to load PersonController, 'm getting this error:

A resource has not been properly defined for type 'Person'. Ensure it has been registered on the ResourceGraph.

Environment

  • JsonApiDotNetCore Version: 3.1.0
  • Other Relevant Package Versions:
@wisepotato
Copy link
Contributor

As the error states, you will ned to register it to the resourcegraph https://json-api-dotnet.github.io/JsonApiDotNetCore/usage/resource-graph.html

@ahmadalli
Copy link
Author

the point is I'm using entity framework discovery method but it doesn't support models built by model builder instead of dbset

@wisepotato
Copy link
Contributor

Ah. That might be true, as we expect there to be a DbSet Available, I don't know if this is an easy fix at the moment. If you have time for investigation you are more than welcome to add a pull request as this does seem like an OK things to support. I don't have much need for this feature as I explicitly define my models.

If we get more requests in this area we can consider devoting some time to it.

@ahmadalli
Copy link
Author

@wisepotato could you please show me some initial point to investigate the issue?

@wisepotato
Copy link
Contributor

wisepotato commented Aug 27, 2019

To be honest? Wait a week or 2, or explicitely state your models for now (if thats possible for you). We are working on decoupling the JsonApiContext (where you're getting your error) and making it more testable and (hint hint) more extendable for different uses.

You're getting the error here:

if (_controllerContext.RequestEntity == null)
throw new JsonApiException(500, $"A resource has not been properly defined for type '{typeof(T)}'. Ensure it has been registered on the ResourceGraph.");

But in our version, that we're working on for V4, the _controllerContext is non-existent. So what I suggest is: either help and see if you can make a basic testable example project (which helps us immensely) and do that in the branch .

New version is here:

https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/feat/context-decoupling/src/JsonApiDotNetCore/Services/JsonApiContext.cs#L80

, as you see the function that throws your errors has been made obsolete. (and for good reason!)

@ahmadalli
Copy link
Author

ahmadalli commented Aug 27, 2019

I found it but I'm not sure how can I fix it.

we need an instance of dbContext here:

public IResourceGraphBuilder AddDbContext<T>() where T : DbContext
{
_usesDbContext = true;
var contextType = typeof(T);
var contextProperties = contextType.GetProperties();
foreach (var property in contextProperties)
{
var dbSetType = property.PropertyType;
if (dbSetType.GetTypeInfo().IsGenericType
&& dbSetType.GetGenericTypeDefinition() == typeof(DbSet<>))
{
var entityType = dbSetType.GetGenericArguments()[0];
AssertEntityIsNotAlreadyDefined(entityType);
var (isJsonApiResource, idType) = GetIdType(entityType);
if (isJsonApiResource)
_entities.Add(GetEntity(GetResourceNameFromDbSetProperty(property, entityType), entityType, idType));
}
}
return this;
}

then we can change AddDbContext to something like this:

public IResourceGraphBuilder AddDbContext<T>() where T : DbContext
{
    _usesDbContext = true;

    var contextType = typeof(T);
    var dbContext = // get the instance of T here
    var types = contextType.GetProperties()
        .Where(t => t.PropertyType.IsGenericType)
        .Where(t => t.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
        .Select(t => t.PropertyType.GetGenericArguments()[0])
        .Union(dbContext.Model.GetEntityTypes().Select(et => et.ClrType))
        .Distinct()
        .ToList();

    foreach (var entityType in types)
    {
        AssertEntityIsNotAlreadyDefined(entityType);

        var (isJsonApiResource, idType) = GetIdType(entityType);
        if (!isJsonApiResource) continue;

        var resourceName = _resourceNameFormatter.FormatResourceName(entityType);
        _entities.Add(GetEntity(resourceName, entityType, idType));
    }

    return this;
}

@wisepotato
Copy link
Contributor

I mean i cant validate if this works, but if you're free to add this to master via a pull request, i welcome it.

@ahmadalli
Copy link
Author

the problem is I don't know how can I get the instance of T in this context. we need access to service provider

@ahmadalli
Copy link
Author

P.S. I've added the properties for now but it'd be good if we solved that issue too

@maurei
Copy link
Member

maurei commented Aug 29, 2019

Hi @ahmadalli, thanks for opening up this issue. It seems like a useful thing to support. Like you mentioned, we need to access the service provider to get an instance of the DbContext, but at the time of calling AddDbContext<T> the service provider has not yet been build. Some thoughts on this:

  1. Possibly we could build an intermediate service provider that we use only here. This needs investigation.
  2. Alternatively, this could just be a design smell, i.e. maybe it suggests we should look in a different direction to solve this issue.

Note that we currently have very limited capacity to work on this. As @wisepotato mentioned, we are currently prioritising #504, which is required for JsonApiDotNetCore to be truly production ready (due to testability).

To advance with your project, I think manually constructing the resource graph
should work. Have you tried this?

If you're interested in really tackling this issue yourself, hit me up in our gitter channel!

@maurei
Copy link
Member

maurei commented Oct 17, 2019

#581 introduces the usage of an intermediate service provider in the IServiceCollectionExtensions we needed in this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants