Code Monkey home page Code Monkey logo

core-admin's Introduction

Core Admin Panel for ASP.NET Core & .NET 8

.NET 8

๐Ÿคฉ Click here to view a live demo on a really slow free Azure Web App instance that might go down at any time!

Fully automatic admin site generator for ASP.NET Core. Add one line of code, get loads of stuff. Features include:

  • A data grid for all your entities
  • Search, filter, sort etc on the grid
  • CRUD screens with validation
  • Binary support for image uploads
  • Foreign key navigation
  • Markdown editor
  • ...and an awesome dark theme!

Screenshot of core admin

Screenshot of core admin

The above screenshots are of the Contoso University sample with Core Admin added to it.

Core Admin scans your app for Entity Framework DB Contexts and makes a nice set of CRUD screens for them.

Setting up with .NET 6 Minimal APIs (version 2.0.0+)

Add via nuget:

dotnet add package CoreAdmin

Add this line before var app = builder.Build(); and after your DbContexts have been added to Services in Program.cs:

builder.Services.AddCoreAdmin();

You need to make sure Endpoints are enabled as they don't appear to be in the default templates. Also static files middleware has to be enabled.

For example, add the following before app.Run();:

app.UseStaticFiles();
app.MapDefaultControllerRoute();

How to use with .NET Core 3.1 and .NET 5 (version <2.0.0)

Add via nuget:

dotnet add package CoreAdmin

Add this line at the bottom of ConfigureServices() (and after your DbContexts have been added to Services) in Startup.cs:

services.AddCoreAdmin();

Running the app

Run your app with with /coreadmin on the end of the URL, for example https://localhost:5001/coreadmin and you'll get the app appearing as shown above.

Security

Basic role based security is currently supported. Whilst this does not need to be set up when running the admin panel in Development mode (for testing), all other environments need this set up.

Role based security

When adding Core Admin, provide the list of Roles required to access the panel, for example:

services.AddCoreAdmin("Administrator");

The admin panel will then use the built in User Principal system to validate the roles. Essentially, if a normal call to User.IsInRole("rolename") would return true, then the user will be able to access the panel.

Custom auth methods

You can also provide a function that will be evaluated on every request to see if the user can access the panel.

For example, in Configure, use the following method (don't use the exact method below as all requests will be refused):

app.UseCoreAdminCustomAuth((serviceProvider) => Task.FromResult(false));

You can change this to your own logic as needed. You can read from cookies or do whatever you need to do, loading services from the serviceProvider.

Binary file uploads

If your entities have byte array Properties, for example the Image property in the following:

 public class TestEntityWithImage
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }

        [Required]
        public string Name { get; set; }

        public byte[]? Image { get; set; }
    }

The package will attempt to render the byte arrays as images on the list view:

Screenshot of core admin

And also provide upload functionality on the Create and Edit views:

Screenshot of core admin

If you don't want this behaviour for a byte array property, make sure to prevent it from rendering in the forms using ScaffoldColumn(false):

[System.ComponentModel.DataAnnotations.ScaffoldColumn(false)]
public byte[]? NotAnImage { get; set; }

Foreign keys

If you have a foreign key in an entity, the create and edit screens will show a drop down list. For example:

public class TestParentEntity
{
    [Key]
    public Guid Id { get; set; }

    [Display(AutoGenerateField = false)] // prevent showing on the grid
    public Guid ChildId { get; set; }

    [ForeignKey("ChildId")]
    public TestChildEntity Child { get; set; }
}

and

public class TestChildEntity
{
    [Key]
    public Guid Id { get; set; }

    public string Name { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

Will result in:

Screenshot of core admin

Make sure to provide a ToString() method on your Entity as this is what will be used in the drop down menu and in the grid!

Markdown Editor

If you want to make a string Property editable with the Markdown editor. You need to add the [DataType("Markdown")] type to it. For example:

[DataType("Markdown")]
public string? Body { get; set; }

Custom URL to the admin panel

To use a custom URL, add this line of code in the ConfigureServices part of Startup.cs

app.UseCoreAdminCustomUrl("customurltoadmin");

you can then access the panel at /customurltoadmin or whatever you need it to be.

Custom app title

To have a custom title of the app, use the following code in Program.cs or Startup.cs:

app.UseCoreAdminCustomTitle("AwesomeTitle");

This will result in something like this:

Screenshot of core admin custom title

Localisation

Localisation is supported with built in JSON files thanks to inspiration from https://github.com/Amine-Smahi/LocaliJson. English, French and Japanese are supported at the moment, please submit a pull request if you would like to add a translation!

Screenshot of core admin custom title

Ignoring certain entity types

You can prevent certain types of entities being shown or available in the admin panel by setting the IgnoreEntityTypes value in the options during AddCoreAdmin(), like so:

services.AddCoreAdmin(new CoreAdminOptions() { IgnoreEntityTypes = new List<Type>() { typeof(DatabaseEntityToIgnore) } });

The above will make it so that DatabaseEntityToIgnore is not shown.

CDN Support for the built in static assets

To use a CDN or serve the built in CSS and JS from another URL, copy the /css and /js folders from src/DotNetEd.CoreAdmin/wwwroot/ to the root of your CDN. Then in Configure in Startup.cs, call the following method:

 app.UseCoreAdminCdn("https://my-cdn-root.com");

The Core Admin Razor Class library will then serve the static assets from this URL root rather than from the built in versions.

Troubleshooting

If you see a 401 error when accessing /coreadmin it means you are running in a non-development environment and you have not set a role up. See "Role based security" above.

Community

You can find in this section some links about community content (writing, code samples, etc...). Don't hesitate to make a PR to add your own.

License

LGPL licensed. Depends on the snazzy NonFactors.Grid.Mvc6 and Bootstrap, both of which are MIT licensed.

Authors

Ed Andersen (@edandersen)

core-admin's People

Contributors

abdomahfoz avatar angelodotnet avatar danroot avatar devantler avatar edandersen avatar gaetandezeiraud avatar iwedaz avatar solrevdev avatar znextage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

core-admin's Issues

Admin not working

StartupSQLite.cs(34,13): error CS1061: 'IServiceCollection' does not contain a definition for 'AddCoreAdmin' and no accessible extension method 'AddCoreAdmin' accepting a first argument of type 'IServiceCollection' could be found (are you missing a using directive or an assembly reference?) [/home/user/projects/net_admin/AspNetCore.Docs/aspnetcore/data/ef-rp/intro/samples/cu30/ContosoUniversitySQLite.csproj]

The build failed. Fix the build errors and run again.

DateTimeOffset causes exception

I have followed installation for .NET 6.

this is the model I have:

public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }

and when trying to view this model in coreadmin there's an error:

ArgumentException: Expression of type 'System.Decimal' cannot be used for return type 'System.Object'

Full stack trace:

System.Linq.Expressions.Expression.ValidateLambdaArgs(Type delegateType, ref Expression body, ReadOnlyCollection<ParameterExpression> parameters, string paramName)
System.Linq.Expressions.Expression.Lambda<TDelegate>(Expression body, string name, bool tailCall, IEnumerable<ParameterExpression> parameters)
AspNetCoreGeneratedDocument.Views_CoreAdminData_Index+<>c__DisplayClass0_0.<ExecuteAsync>b__0(IGridColumnsOf<object> columns)
NonFactors.Mvc.Grid.HtmlGridExtensions.Build<T>(IHtmlGrid<T> html, Action<IGridColumnsOf<T>> builder)
AspNetCoreGeneratedDocument.Views_CoreAdminData_Index.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Thank you

Custom response

How can I create a custom response for unauthorized response instead of 401 ?
For example redirect to "Access Denied" page

if (!Model.Rows.Any() causes re-execution of the select statement against SqlServer Table

In the partial _Grid.cshtml there is an if statement, that check if there any rows, in order to display an "Empty table message"

@if (!Model.Rows.Any() && Model.EmptyText != null)

The Model.Rows.Any() causes the re-execution of the query against the database.
After commenting the section the database will be called only one time.

I will search a better way to expose the Zero rows message.

Translations support

Ability to add translations for: column names, tables, buttons, filter conditions, Core Admin label.
Ability to set a custom path to CoreAdmin.

Autogenerate ID in create

Disable field for autogenerated primary keys in a create view.

it is inconvenient to create a new record and fill in the ID yourself.

Overridding views - ImageUtils is marked as internal

When overriding views for customisation (themeing etc), the images cannot be displayed due to the ImageUtils class being marked as internal.

Currently the only way to get past this is to copy the code and have a copy of it in the main application.

Working with MongoDB

If you use MongoDB, the collections are not defined when you go to the admin page. The menu on the left shows only the Home button. Is there any way to add them yourself?

Error with Npgsql

I use a special package Npgsql to work with PostgreSQL. And I have all models loaded normally except one and the most important one.
Here is the composition of the model:

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public Guid GuidId { get; set; }

        public virtual EventType Type { get; set; }

        public string Title { get; set; }

        public string Desc { get; set; }

        public Guid Author { get; set; }

        public bool IsAllowed { get; set; }

        public DateTime DateStart { get; set; }

        public DateTime DateEnd { get; set; }

        public string Place { get; set; }

        public Guid Responsible { get; set; }

        public DateTime DateCreated { get; set; }

        public DateTime DateUpdated { get; set; }

        public virtual ICollection<Eventะกomment> Comments { get; set; }

        public virtual ICollection<EventWorker> Workers { get; set; }

        public virtual ICollection<EventAdministration> Administrations { get; set; }

        public virtual ICollection<EventInvitation> invitations { get; set; }

The error I'm getting:
NpgsqlOperationInProgressException: A command is already in progress: SELECT

Show page for the Records

Hey ho!
It's me again.

Edandersen, do you have any plan to add a show view for the records? If you allow me, let me suggest something:
In Ruby on Rails we have a lib called "Administrate" it's a robust admin panel. There we can navigate in the records through the foreign keys. You can check how it works in this demo:

https://administrate-demo.herokuapp.com/admin
Administrate in Github

The lib is powered by Thoughtbot, a very nice company.

It would be amazing in .Net.

Thank you so much!!

Null Foreign Key

Thanks for this project - it's saving me a ton of time. Currently, I have models with foreign keys that are null by default. However, if I open one to edit it, the foreign key dropdown doesn't allow for a null entry and defaults to the first option. As a result, I can't edit the record without also setting the foreign key to a non-null value.

Of course, as a workaround, I can create default instances that serve the same purpose as null.

Dark theme for the dashboard

A good idea would be to set up a dark theme in addition to the default light.
I am working on it currently. If somebody is interested, don't hesitate to ping me!

I have planned to make a PR when done. To see later if it could be integrated into master.

Support for DateTimeOffset

Is there anything special we need to do to format DateTimeOffsets? [DisplayFormat] doesn't seem to be recognized or working.

[ScaffoldColumn(false)] [DisplayFormat(DataFormatString = "{0:yyyy-MMM-dd}")] public DateTimeOffset ModifiedDate { get; set; }

InvalidOperationException: This operation is only valid on generic types.

Hello,

I get this error when trying to Edit a user, which inherits from IdentityUser. This is the error I get:

System.InvalidOperationException: This operation is only valid on generic types.
at System.RuntimeType.GetGenericTypeDefinition()
at DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.<>c__DisplayClass3_1.b__1(PropertyInfo p)
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable1 source, Func2 predicate, Boolean& found)
at DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.GetDbSetValueOrNull(String dbSetName, DbContext& dbContextObject, Type& typeOfEntity, Dictionary2& relationships) at DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.GetEntityFromDbSet(String dbSetName, String id, DbContext& dbContextObject, Type& typeOfEntity, Dictionary2& relationships)
at DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.EditEntity(String dbSetName, String id)
at lambda_method245(Closure , Object , Object[] )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Not nullable foreign keys on entity cause error

Only nullable foreign keys in entity works as expected - empty field in dropdown, etc.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(AutoGenerateField = false)]
public int Id { get; set; }

public string Name { get; set; }

[Display(AutoGenerateField = false)] // prevent showing on the grid
public int? CategoryId { get; set; }

[ForeignKey("CategoryId")]
public Category? Category { get; set; }

Declaring foreign key without question mark makes core admin not being able to add entity.

Bug: Duplicated tables

When I set this admin panel up with a DbContext that extends ApiAuthorizationDbContext<ApplicationUser> to authenticate with IdentityServer, I get duplicated DbSets.

image

Is there a way to ensure it does not list DbSet's if they are already listed? Or filter DbSets, to only show e.g. Users and Roles as well as my custom DbSets such as Ecosystems.

EDIT:
I think the duplicated DbSets are caused by this code when a user inherits from a subtype of DbContext:

private static void FindDbContexts(IServiceCollection services)
{
    foreach (var service in services.ToList())
    {
        if (service.ImplementationType == null)
            continue;
        if (service.ImplementationType.IsSubclassOf(typeof(DbContext)))
        {
            services.AddTransient(services => new DiscoveredDbContextType() { Type = service.ImplementationType });
        }
    }
}

I suspect the services collection contains multiple references to the implemented DbContext so it can be resolved from all its inherited interfaces. As such, I believe a DiscoveredDbContextis added for each found subtype of DbContext even though they reference the same implemented DbContext. Maybe a check can be made to check whether a DbContext has already been processed, and in that case, it can be skipped, so the DbSets are no longer duplicated.

I hope this helps! :-)

System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values.

Trying to view a table with many NULL fields (Allow nulls checked in table designer) i receive the following error

System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values.
at Microsoft.Data.SqlClient.SqlBuffer.ThrowIfNull()
at Microsoft.Data.SqlClient.SqlBuffer.get_String()
at Microsoft.Data.SqlClient.SqlDataReader.GetString(Int32 i)
at lambda_method84(Closure , QueryContext , DbDataReader , ResultContext , SingleQueryResultCoordinator )
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.MoveNext() at System.Linq.Enumerable.SelectIterator[TSource,TResult](IEnumerable1 source, Func`3 selector)+MoveNext()
at AspNetCoreGeneratedDocument.Views_Shared_MvcGrid__Grid.ExecuteAsync()

it seems that MvcGrid tries to get a value from a null field.

There is any suggestino to avoid this problem?

Thank You.

Serve static file CDN support

I would like to serve static file from a cdn like AWS S3 or cloud front.
I will fork your repository and try solution.

Foreign key relationship in .Net 5.0

Hey guys.

I'm trying to add the DataNotation of Relationship in the Models and "[Display(AutoGenerateField = false)]" and "[ForeignKey("ChildId")]". But it doesn't appears in the dropdown list style.

It works in .net 5.0 or just in .net 6?

Thanks!

Show foreign table NAME instead of ID on grid view

In the edit view this was done possible (good job) by overriding the ToString function on entity model. Is it also possible to implement this also on grid view?

It would be also nice if the foreign column could be a clickable link that redirect the user to the foreign table, that is filtered to the specific row clicked on the parent table ๐Ÿ’ช

Add to README.md

Make sure that there is a line in your Program.cs that says:

app.UseStaticFiles();

Else the css and js files are not published to clients.

I had to add this line manually because my project is dot net core web api and this line is not added in this type of project.

Image upload

As topic.
Options at configuration of CoreAdmin can be:

  • Database
  • Physical storage
  • Cloud data storage service (in the future, don't seems the most important)

Double type verification problem

Hi,
I have updated to the last version. No matter the value (except integer), a double input show The field Price must be a number..
At the beginning, I thinking of a problem of localization (my browser is in French. So it used a comma, not a dot. But even with a dot, the problem is the same.

Here a screenshot of the DemoApp (in my fork, but the problem is the same with the 1.7.0).
image

Problems type

byte
enum

example

public class MyClass
{
	public enum States { Free, Busy }

	public int Id { get; set; }
	
	public States State { get; set; }
	
}

or

public class MyClass
{
	public int Id { get; set; }
	
	public MyClassStates State { get; set; }
	
}

public enum MyClassStates { Free, Busy }
  1. Error => Views\CoreAdminData\Index.cshtml
else if (entityProperty.PropertyType == typeof(double))
{
    var lambda = Expression.Lambda<Func<object, double>>(property, entity);

    columns.Add(lambda).Titled(entityProperty.Name);
}
  1. Error => Create
ArgumentNullException: Value cannot be null. (Parameter 'propertyInfo')
System.Reflection.NullabilityInfoContext.Create(PropertyInfo propertyInfo)
DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.GetDbSetValueOrNull(string dbSetName, out DbContext dbContextObject, out Type typeOfEntity, out Dictionary<string, Dictionary<object, string>> relationships) in CoreAdminDataController.cs
+
                                var nullabilityInfo = _nullabilityContext.Create(typeOfEntity.GetProperty(f.Name));
DotNetEd.CoreAdmin.Controllers.CoreAdminDataController.Create(string id) in CoreAdminDataController.cs
+
            var dbSetValue = GetDbSetValueOrNull(id, out var dbContextObject, out var entityType, out var relationships);
lambda_method212(Closure , object , object[] )

Foreign Key Navigation

If you scaffold from database, ie database first approach then foreign key navigation doesnt really work

How do you get the DB provider? pgsql instead of mysql?

I use pgsql with

  • Microsoft.AspNetCore.Identity.EntityFrameworkCore 6.0.6
  • Microsoft.EntityFrameworkCore 6.0.6
  • Microsoft.EntityFrameworkCore.Design 6.0.6
  • Microsoft.EntityFrameworkCore.Sqlite 6.0.6
  • Microsoft.EntityFrameworkCore.Sqlite.Core 6.0.6
  • Microsoft.EntityFrameworkCore.Tools 6.0.6
  • Pomelo.EntityFrameworkCore.MySql 6.0.1
  • Npgsql.EntityFrameworkCore.PostgreSQL 6.0.4

I have some context classes set to work with a postgres, and one set to Mysql.
I looked all the code on the git, but I didn't find the reference to mysql, nor to pomelo
When I loaded the core-admin, some tables (postgres) work, some others (also postgres) leave the following exception. All tables are linked to the same DbContext.

MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, MySqlConnection connection, int startTickCount, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in ServerSession.cs, line 433

Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.Execute<TState, TResult>(TState state, Func<DbContext, TState, TResult> operation, Func<DbContext, TState, ExecutionResult<TResult>> verifySucceeded)

MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, MySqlConnection connection, int startTickCount, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in ServerSession.cs
MySqlConnector.Core.ConnectionPool.ConnectSessionAsync(MySqlConnection connection, string logMessage, int startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in ConnectionPool.cs
System.Threading.Tasks.ValueTask<TResult>.get_Result()
MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, int startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in ConnectionPool.cs
MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, int startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in ConnectionPool.cs
System.Threading.Tasks.ValueTask<TResult>.get_Result()
MySqlConnector.MySqlConnection.CreateSessionAsync(ConnectionPool pool, int startTickCount, Nullable<IOBehavior> ioBehavior, CancellationToken cancellationToken) in MySqlConnection.cs
System.Threading.Tasks.ValueTask<TResult>.get_Result()
MySqlConnector.MySqlConnection.OpenAsync(Nullable<IOBehavior> ioBehavior, CancellationToken cancellationToken) in MySqlConnection.cs
MySqlConnector.MySqlConnection.Open() in MySqlConnection.cs
Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(bool errorsExpected)
Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(bool errorsExpected)
Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(bool errorsExpected)
Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlRelationalConnection.Open(bool errorsExpected)
Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable<T>+Enumerator.InitializeReader(Enumerator enumerator)
Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable<T>+Enumerator+<>c.<MoveNext>b__19_0(DbContext _, Enumerator enumerator)
Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.Execute<TState, TResult>(TState state, Func<DbContext, TState, TResult> operation, Func<DbContext, TState, ExecutionResult<TResult>> verifySucceeded)

InvalidOperationException: An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure()' to the 'UseMySql' call.
Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.Execute<TState, TResult>(TState state, Func<DbContext, TState, TResult> operation, Func<DbContext, TState, ExecutionResult<TResult>> verifySucceeded)
Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable<T>+Enumerator.MoveNext()
System.Linq.Enumerable.TryGetSingle<TSource>(IEnumerable<TSource> source, out bool found)
lambda_method800(Closure , QueryContext )
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute<TResult>(Expression query)
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute<TResult>(Expression expression)
System.Linq.Queryable.Count<TSource>(IQueryable<TSource> source)
NonFactors.Mvc.Grid.GridPager<T>.Process(IQueryable<T> items)
NonFactors.Mvc.Grid.GridRows<T>.GetEnumerator()
AspNetCoreGeneratedDocument.Views_Shared_MvcGrid__Grid.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.RenderPartialCoreAsync(string partialViewName, object model, ViewDataDictionary viewData, TextWriter writer)
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.PartialAsync(string partialViewName, object model, ViewDataDictionary viewData)
Microsoft.AspNetCore.Mvc.Rendering.HtmlHelperPartialExtensions.Partial(IHtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData)
Microsoft.AspNetCore.Mvc.Rendering.HtmlHelperPartialExtensions.Partial(IHtmlHelper htmlHelper, string partialViewName, object model)
NonFactors.Mvc.Grid.HtmlGrid<T>.WriteTo(TextWriter writer, HtmlEncoder encoder)
Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.WriteToAsync(TextWriter writer, HtmlEncoder encoder)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|28_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Feature proposal: Enable filtering what tables to show

In some cases showing all tables can be confusing for the end-user, and it would be nice if we were able to filter what tables are shown in the admin panel, such that only the stuff relevant to the person using the admin panel is shown.

I suggest adding an inclusive or exclusive list to the AddCoreAdminextension method parameters that exclude or include all matching DbSets found in the list. If this is an optional parameter, it can default to the current behavior if left out of the call.

Ps. I removed all info regarding this issue in my other issue (#10), so issue #10 is only concerned with duplicated tables.

MissingMethodException if the Entity only contains foreign key column

I using the chinook sample DB to trying core admin, the (PlaylistTrack) table reference to (PlayList) and (Track) tables, and I have check both of this table is work fine on core admin,
And the DB schema:

CREATE TABLE [PlaylistTrack]
(
    [PlaylistId] INTEGER  NOT NULL,
    [TrackId] INTEGER  NOT NULL,
    CONSTRAINT [PK_PlaylistTrack] PRIMARY KEY  ([PlaylistId], [TrackId]),
    FOREIGN KEY ([PlaylistId]) REFERENCES [Playlist] ([PlaylistId]) 
		ON DELETE NO ACTION ON UPDATE NO ACTION,
    FOREIGN KEY ([TrackId]) REFERENCES [Track] ([TrackId]) 
		ON DELETE NO ACTION ON UPDATE NO ACTION
)

The EntityClass:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ApplicationCore.Entities
{
	[Table("PlaylistTrack")]
	public class PlayListTrack
	{
		private long _PlayListId;
		[Display(AutoGenerateField = false)] // prevent showing on the grid
		public long PlayListId
		{
			get { return _PlayListId; }
			set { _PlayListId = value; }
		}

		private long _TrackId;
		[Display(AutoGenerateField = false)] // prevent showing on the grid
		public long TrackId
		{
			get { return _TrackId; }
			set { _TrackId = value; }
		}

		[ForeignKey("PlayListId")]
		public virtual PlayList? PlayList { get; set; }

		[ForeignKey("TrackId")]
		public virtual Track? Track { get; set; }

		public PlayListTrack(long playListId, long trackId)
		{
			this.PlayListId = playListId;
			this.TrackId = trackId;
		}
	}
}

The RepositoryContext class as well:

using Microsoft.EntityFrameworkCore;

namespace ApplicationCore.Entities
{
    public class RepositoryContext : DbContext
    {
        public RepositoryContext(DbContextOptions<RepositoryContext> options) : base(options)
        {
        }
        public DbSet<PlayList>? PlayLists { get; set; }
        public DbSet<PlayListTrack>? PlayListTracks { get; set; }
        public DbSet<Track>? Tracks { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<PlayListTrack>().HasKey(table => new {
                table.PlayListId,
                table.TrackId
            });
        }

    }
}

The PlaylistTrack entity display is fine, but missing edit button and if I press the Create button, an unhandled exception occurred:
image

image

I guess because the primary key contains multiple columns and Core Admin don't handle this key type?

Host a demo on Microsoft Azure free tier

Why not host the DotNetEd.CoreAdmin.DemoAppDotNet6 project on Microsoft Azure free tier? As a demo.
Don't know if it is possible to reset data automatically.

Possible to add support for MetadataTypeAttribute?

    [MetadataType(typeof(SalesPersonMetadata))]
    [Table("SalesPerson")]
    public partial class SalesPerson
    {
        [Key]
        public int Id { get; set; }
        [StringLength(100)]
        public string Name { get; set; } = null!;
        [Column(TypeName = "datetime")]
        public DateTime CreatedDate { get; set; }
        public bool Deactivated { get; set; }
    }


    public class SalesPersonMetadata
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    }

Validation support

Hello.

I've just seen this project so there might already be something in place, but is there a way to define custom validation to enforce business rules on creating and editing entities? An integration with FluentValidation would be a nice.

select list support

It would be nice if instead of having to write the foreign key, you can select from a select list of all the related objects (it will take more time to load all the objects of course).
You can use ToString() of an object to represent them in the View and let the developer override the ToString method for each entity.

Override MVC Grid Templates

I have noticed that with a new dotnet 6 project (haven't tested any others) that the admin panel is now served as an SPA of some sort, how would one go about overriding/customising the MVC-Gird views and styles?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.