Skip to content

Commit

Permalink
add marketDemo project
Browse files Browse the repository at this point in the history
  • Loading branch information
oliver021 committed May 15, 2022
1 parent d43d63b commit 145c480
Show file tree
Hide file tree
Showing 24 changed files with 908 additions and 127 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ The easiest way to install EntityDock in your project is to install the latest E

It's possible that more packages may be added in the future.



You can install these package following the next example:

```bash
Install-Package EntityDock.Lib.Auto
```

or using dotnet CLI:

```bash
dotnet add package EntityDock.Lib.Auto
```



# Key questions

**What's mean "generate controller"?** Yes, without writing a line of code or declaring a class you can have API Controllers base on ASP.NET Core MVC from declared entities. The code required for this is as follows:
Expand All @@ -76,6 +92,7 @@ When you are setting up your MVC options in ASP.NET Core, you must call this met
```c#
[SetRouteAttibute("data/students")]
public class StudentEntity{

public uint Id {get;set;}

public string Name {get;set;}
Expand All @@ -88,11 +105,9 @@ public class StudentEntity{
}
```

Then you will have a complete API Rest about this entity with full methods, Crud, search, filters, sort and more.

**How works the `AutoDbContext`?** It's simple, this is a class that derived from `DbContext` in Entity Framework, then using this class, you can create a context from external assemblies or types collections that will has these types as entities and this context can be used like other any context of Entity Framework. Using this way you cannot setup via `ModelBuilder` API fluent methods inside context class, you just have conventions and annotations for `AutoDbContext`. This is a natural limitations 'cause its job consists of including different entities without declare specific `DbContext`.

Then you will have a complete API Rest about this entity with full methods, Crud, search, filters, sort and more. Of course you should care about your DB connections and migrations if you are using relational database. This package is completely compatible with Entity Framework Core.

**How works the `AutoDbContext`?** It's simple, this is a class that derived from `DbContext` in Entity Framework Core, then using this class, you can create a context from external assemblies or types collections that will has these types as entities and this context can be used like other any context of Entity Framework. Using this way you cannot setup via `ModelBuilder` API fluent methods inside context class, you just have conventions and annotations for `AutoDbContext`. This is a natural limitations 'cause its job consists of including different entities without declare specific `DbContext`.

## Contributing

Expand Down
13 changes: 13 additions & 0 deletions src/Libs/Auto/AutoApiOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace EntityDock.Lib.Auto
{
public class AutoApiOption
{
public bool ApiUsageService { get; set; } = false;

public bool SchemaShareEnabled { get; set; } = false;
}
}
133 changes: 133 additions & 0 deletions src/Libs/Auto/AutoDbExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using EntityDock.Lib.Base;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;
using EntityDock.Lib.Auto;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
public static class AutoDbExtensions
{
/// <summary>
/// Add auto controllers features in services collections
/// </summary>
/// <param name="services"></param>
/// <param name="types"></param>
public static void AddDataControllers(this IMvcCoreBuilder services, Type[] types, AutoApiOption options = null)
{
// feature to add controller frome ntity types
services.PartManager.FeatureProviders.Add(new ControllerMakerFeatureProvider(types, options));

// convention to use in routes
services.AddMvcOptions(x => x.Conventions.Add(new GenericControllerFeatureConvention()));
}

/// <summary>
/// Add auto controllers features in services collections
/// </summary>
/// <param name="services"></param>
/// <param name="types"></param>
public static void AddDataControllers(this IMvcBuilder services, Type[] types, AutoApiOption options = null)
{
// feature to add controller frome ntity types
services.PartManager.FeatureProviders.Add(new ControllerMakerFeatureProvider(types, options));

// convention to use in routes
services.AddMvcOptions(x => x.Conventions.Add(new GenericControllerFeatureConvention()));
}

/// <summary>
/// Add auto controllers features in services collections
/// </summary>
/// <param name="services"></param>
/// <param name="dbContext"></param>
public static void AddDataControllers(this IMvcCoreBuilder services,
Type dbContext,
bool deepScan = false,
AutoApiOption options = null)
{
if (!dbContext.IsAssignableFrom(typeof(DbContext)))
{
throw new Exception();
}

var types = dbContext.GetProperties()
.Where(x => x.PropertyType.Name.Equals(typeof(DbSet<>).Name))
.Select(x => x.PropertyType.GenericTypeArguments[0])
.ToArray();

// feature to add controller frome ntity types
services.PartManager.FeatureProviders.Add(new ControllerMakerFeatureProvider(types, options));

// convention to use in routes
services.AddMvcOptions(x => x.Conventions.Add(new GenericControllerFeatureConvention()));
}

/// <summary>
/// Add auto controllers features in services collections
/// </summary>
/// <param name="services"></param>
/// <param name="dbContext"></param>
public static void AddDataControllers(this IMvcBuilder services,
Type dbContext,
bool deepScan = false,
AutoApiOption options = null)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}

if (dbContext is null)
{
throw new ArgumentNullException(nameof(dbContext));
}

var types = dbContext.GetProperties()
.Where(x => x.PropertyType.Name.Equals(typeof(DbSet<>).Name))
.Select(x => x.PropertyType.GenericTypeArguments[0])
.ToArray();

// feature to add controller frome ntity types
services.PartManager.FeatureProviders.Add(new ControllerMakerFeatureProvider(types, options));

// convention to use in routes
services.AddMvcOptions(x => x.Conventions.Add(new GenericControllerFeatureConvention()));
}

/// <summary>
/// Add auto controllers features in services collections using a passed type argument.
/// </summary>
/// <typeparam name="TContext"></typeparam>
/// <param name="services"></param>
/// <param name="deepScan"></param>
public static void AddDataControllers<TContext>(this IMvcCoreBuilder services, bool deepScan = false)
{
services.AddDataControllers(typeof(TContext), deepScan);
}

/// <summary>
/// Add auto controllers features in services collections using a passed type argument.
/// </summary>
/// <typeparam name="TContext"></typeparam>
/// <param name="services"></param>
/// <param name="deepScan"></param>
public static void AddDataControllers<TContext>(this IMvcBuilder services, bool deepScan = false)
{
services.AddDataControllers(typeof(TContext), deepScan);
}

/// <summary>
/// Add auto controllers features in services collections
/// </summary>
/// <param name="services"></param>
public static void AddFilterTriggers(this IMvcCoreBuilder services, Type[] types)
{
services.AddMvcOptions(opt => opt.Filters.Add<EntityEventFilter>());
}
}
}
56 changes: 38 additions & 18 deletions src/Libs/Auto/ControllerMakerFeatureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

namespace EntityDock.Lib.Auto
{

/// <summary>
/// Create controller by passed types as controller
/// </summary>
Expand All @@ -25,7 +24,6 @@ public static ControllerMakerFeatureProvider FromAssembly(Assembly assembly)
{
// filter and take an array of the candidates
return new ControllerMakerFeatureProvider(types: assembly.GetExportedTypes()
.Where(t => t.IsEntity())
.Where(t => t.IsDefined(typeof(SetRouteAttibute)))
.ToArray()
);
Expand All @@ -35,15 +33,22 @@ public static ControllerMakerFeatureProvider FromAssembly(Assembly assembly)
/// Require entry types for mapping
/// </summary>
/// <param name="types"></param>
public ControllerMakerFeatureProvider(Type[] types)
public ControllerMakerFeatureProvider(Type[] types, AutoApiOption options = default)
{
TargetTypes = types;
if (options is null)
{
options = new AutoApiOption();
}

TargetTypes = types ?? throw new ArgumentNullException(nameof(types));
Options = options;
}

/// <summary>
/// All passed types
/// </summary>
public Type[] TargetTypes { get; }
public AutoApiOption Options { get; }

/// <summary>
/// Populate all controller created from passed types
Expand All @@ -57,7 +62,7 @@ public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeatur
{
// push new controller from route
feature.Controllers.Add(item: GetCandidateController(route)
.MakeGenericType(route.Model)
.MakeGenericType(route.Model, HelpersExtensions.FindKeyType(route.Model))
.GetTypeInfo()
);
}
Expand All @@ -71,9 +76,10 @@ public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeatur
private Type GetCandidateController(UnitRoute item)
=> item.ModelType switch
{
ModelType.FullyFeatures => typeof(FullyFeatureController<,>),
ModelType.Record => typeof(RecordController<,>),
//ModelType.Record => typeof(),
ModelType.FullyFeatures when(Options.ApiUsageService) => typeof(FullyFeatureController<,>),
ModelType.Record when(Options.ApiUsageService) => typeof(RecordController<,>),
ModelType.FullyFeatures => typeof(RepoFullyFeatureController<,>),
ModelType.Record => typeof(RepoRecordController<,>),
_ => null
};

Expand All @@ -87,17 +93,31 @@ private IEnumerable<UnitRoute> GetRoutes()
{
var attr = target.GetCustomAttribute<EntityAttribute>();

// make an route from attributes specifications
return new UnitRoute {
Model = target,
ModelType = attr.Usage switch
// if has attribute
if (attr != null)
{
// make an route from attributes specifications
return new UnitRoute
{
Model = target,
ModelType = attr.Usage switch
{
EntityUsage.Readonly => ModelType.Readonly,
EntityUsage.Record => ModelType.Record,
EntityUsage.FullyUsage => ModelType.FullyFeatures,
_ => throw new InvalidOperationException()
}
};
}
else
{
// from static definition
return new UnitRoute
{
EntityUsage.Readonly => ModelType.Readonly,
EntityUsage.Record => ModelType.Record,
EntityUsage.FullyUsage => ModelType.FullyFeatures,
_ => throw new InvalidOperationException()
}
};
Model = target,
ModelType = ModelType.FullyFeatures
};
}
});
}
}
Expand Down
Loading

0 comments on commit 145c480

Please sign in to comment.