All Projects → urfnet → Urf.core.sample

urfnet / Urf.core.sample

URF.Core Sample Solution - E2E sample built with ASP.NET Core, Entity Framework Core, URF.Core, Angular, Kendo UI & OData Core. Live demo: https://goo.gl/QpJVgd

Projects that are alternatives of or similar to Urf.core.sample

Kodkod
https://github.com/alirizaadiyahsi/Nucleus Web API layered architecture startup template with ASP.NET Core 2.1, EF Core 2.1 and Vue Client
Stars: ✭ 45 (+12.5%)
Mutual labels:  best-practices, design-patterns, entity-framework-core, asp-net-core
Onion Architecture Asp.net Core
WhiteApp API solution template which is built on Onion Architecture with all essential feature using .NET 5!
Stars: ✭ 196 (+390%)
Mutual labels:  entity-framework-core, asp-net-core, repository
Urf.core
Unit of Work & Repositories Framework - .NET Core, NET Standard, Entity Framework Core. 100% extensible & lightweight. Live demo: https://goo.gl/QpJVgd
Stars: ✭ 226 (+465%)
Mutual labels:  entity-framework-core, design-patterns, repository
Hexagonal-architecture-ASP.NET-Core
App generator API solution template which is built on Hexagnonal Architecture with all essential feature using .NET Core
Stars: ✭ 57 (+42.5%)
Mutual labels:  repository, entity-framework-core, asp-net-core
Run Aspnetcore
A starter kit for your next ASP.NET Core web application. Boilerplate for ASP.NET Core reference application, demonstrating a layered application architecture with applying Clean Architecture and DDD best practices. Download 100+ page eBook PDF from here ->
Stars: ✭ 227 (+467.5%)
Mutual labels:  entity-framework-core, best-practices, design-patterns
generic-for-core
🏗️ Generic Repository & UOW Pattern For ASP.NET Core
Stars: ✭ 55 (+37.5%)
Mutual labels:  design-patterns, entity-framework-core, asp-net-core
BetterRepository
Better Enhanced Repository Pattern Implementation in .NET C#
Stars: ✭ 27 (-32.5%)
Mutual labels:  repository, best-practices, design-patterns
AngularCLI-ASPNET-Core-CustomersService
Example of integrating Angular with ASP.NET Core RESTful Services
Stars: ✭ 61 (+52.5%)
Mutual labels:  entity-framework-core, asp-net-core
Notify.Me
Simple host application to provide send/receive feature for any kind of notifications and messages between client(s) and the host. The application is based on ASP.NET Core and SignalR to demostrate some features of these things...
Stars: ✭ 28 (-30%)
Mutual labels:  entity-framework-core, asp-net-core
Jetweet
Jetweet is a mini twitter clone with basic functionalities, Made using ASP.NET CORE and Entity framework technologies
Stars: ✭ 29 (-27.5%)
Mutual labels:  entity-framework-core, asp-net-core
Asp.net Core Inventory Order Management System
Project example Asp.Net Core Mvc implementation of inventory order management system. warehouse, product, vendor, customer, purchase order, sales order, shipment, goods receive and more.
Stars: ✭ 301 (+652.5%)
Mutual labels:  entity-framework-core, asp-net-core
Volvox.Helios-old
Powerful, modular, web-managed, open-source Discord bot created by a community for communities.
Stars: ✭ 51 (+27.5%)
Mutual labels:  entity-framework-core, asp-net-core
chatle
chat le with ASP.NET Core
Stars: ✭ 20 (-50%)
Mutual labels:  entity-framework-core, asp-net-core
Javascript Patterns
A collection of design patterns and best practices for the JavaScript programming language.
Stars: ✭ 36 (-10%)
Mutual labels:  best-practices, design-patterns
WebApiJwt
Asp.NET Core 2.0 WebApi JWT Authentication with Identity & MySQL
Stars: ✭ 118 (+195%)
Mutual labels:  entity-framework-core, asp-net-core
Nucleus
Vue startup application template that uses ASP.NET Core API layered architecture at the back-end and JWT based authentication
Stars: ✭ 276 (+590%)
Mutual labels:  entity-framework-core, asp-net-core
Simplcommerce
A simple, cross platform, modularized ecommerce system built on .NET Core
Stars: ✭ 3,474 (+8585%)
Mutual labels:  entity-framework-core, asp-net-core
Django Service Objects
Service objects for Django
Stars: ✭ 289 (+622.5%)
Mutual labels:  service, design-patterns
Angular Asp.netcorewebapi Mysql Crud Project
Angular 5 + ASP.Net Core 2.0 WebAPI + MySQL CRUD Sample
Stars: ✭ 22 (-45%)
Mutual labels:  entity-framework-core, asp-net-core
Starwars
GraphQL 'Star Wars' example using GraphQL for .NET, ASP.NET Core, Entity Framework Core
Stars: ✭ 559 (+1297.5%)
Mutual labels:  entity-framework-core, asp-net-core

URF.Core.Sample    Build Status NuGet Badge

Unit-of-Work & Repository Framework | Official URF, Trackable Entities & Design Factory Team

Build history

Docs: comming soon | Subscribe URF Updates: @lelong37 | NuGet: goo.gl/WEn7Jm

Live Demo (Microsoft Azure) (Demo Site is down due to heavy traffic and Azure costs.)

Running & Debugging URF.Core.Sample Locally

URF sample and usage in ASP.NET Core Web API & OData (goo.gl/URdYa1)

Northwind.Api\OData\ProductsController.cs

  • Inject IProductsService (Service Pattern)
  • Inject IUnitOfWork (UnitOfWork Pattern)
  • Using Task, Async, Await as defacto strategy for maximum thread optimization and thread avalibility to handle a maximum number of concurrent HTTP requests without blocking
public class ProductsController : ODataController
{
    private readonly IProductService _productService;
    private readonly IUnitOfWork _unitOfWork;

    public ProductsController(
        IProductService productService,
        IUnitOfWork unitOfWork)
    {
        _productService = productService;
        _unitOfWork = unitOfWork;
    }

    // e.g. GET odata/Products?$skip=2&$top=10
    [EnableQuery]
    public IQueryable<Products> Get() => _productService.Queryable();

    // e.g.  GET odata/Products(37)
    public async Task<IActionResult> Get([FromODataUri] int key)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var product = await _productService.FindAsync(key);

        if (product == null)
            return NotFound();

        return Ok(product);
    }

    // e.g. PUT odata/Products(37)
    public async Task<IActionResult> Put([FromODataUri] int key, [FromBody] Products products)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        if (key != products.ProductId)
            return BadRequest();

        _productService.Update(products);

        try
        {
            await _unitOfWork.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!await _productService.ExistsAsync(key))
                return NotFound();
            throw;
        }

        return NoContent();
    }

    // e.g. PUT odata/Products
    public async Task<IActionResult> Post([FromBody] Products products)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        _productService.Insert(products);
        await _unitOfWork.SaveChangesAsync();

        return Created(products);
    }

    // e.g. PATCH, MERGE odata/Products(37)
    [AcceptVerbs("PATCH", "MERGE")]
    public async Task<IActionResult> Patch([FromODataUri] int key, [FromBody] Delta<Products> product)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var entity = await _productService.FindAsync(key);
        if (entity == null)
            return NotFound();

        product.Patch(entity);
        _productService.Update(entity);

        try
        {
            await _unitOfWork.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!await _productService.ExistsAsync(key))
                return NotFound();
            throw;
        }
        return Updated(entity);
    }

    // e.g. DELETE odata/Products(37)
    public async Task<IActionResult> Delete([FromODataUri] int key)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var result = await _productService.DeleteAsync(key);

        if (!result)
            return NotFound();

        await _unitOfWork.SaveChangesAsync();

        return StatusCode((int) HttpStatusCode.NoContent);
    }
}

Northwind.Api\Startup.cs

  • URF.Core DI & IoC Configuration/Registration Bindings
  • JSON Serialization & Deserialization Cyclical Configuration
  • ASP.NET Core OData Model Configuration
  • ASP.NET Core OData Route Configuraiton
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors();

        services.AddMvc();

        services.AddMvc()
           .AddJsonOptions(options =>
               options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.All);

        services.AddOData();

        var connectionString = Configuration.GetConnectionString(nameof(NorthwindContext));
        services.AddDbContext<NorthwindContext>(options => options.UseSqlServer(connectionString));
        services.AddScoped<DbContext, NorthwindContext>();
        services.AddScoped<IUnitOfWork, UnitOfWork>();
        services.AddScoped<ITrackableRepository<Products>, TrackableRepository<Products>>();
        services.AddScoped<IProductService, ProductService>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();

        app.UseCors(builder =>
        {
            builder.AllowAnyOrigin();
            builder.AllowAnyHeader();
            builder.AllowAnyMethod();
            builder.AllowCredentials();
            builder.Build();
        });

        var oDataConventionModelBuilder = new ODataConventionModelBuilder(app.ApplicationServices);
        var entitySetConfiguration = oDataConventionModelBuilder.EntitySet<Products>(nameof(Products));        
        entitySetConfiguration.EntityType.HasKey(x => x.ProductId);
        entitySetConfiguration.EntityType.Ignore(x => x.Category);
        entitySetConfiguration.EntityType.Ignore(x => x.Supplier);
        entitySetConfiguration.EntityType.Ignore(x => x.OrderDetails);

        app.UseMvc(routeBuilder =>
            {
                routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(1000).Count();
                routeBuilder.MapODataServiceRoute("ODataRoute", "odata", oDataConventionModelBuilder.GetEdmModel()); 
                routeBuilder.EnableDependencyInjection();
            }
        );
    }
}

Implementing Domain Logic with URF Service Pattern

  • All Methods are Virtual and Overridable as place holders for domain specific implementation business logic. This is the preferred implementation strategy for common developer uses cases e.g. adding any pre or post logic after inserting, updating or deleting.
  • Recommended and preferred all Web API Controllers initially injected using Service Pattern from the start. Service Pattern provides a layer for domain logic to reside as the application evolves over time.
  • A natural side effect of the Servie Pattern, is eliminating any potential opportunities for leaky domain implemntations ending up in Controllers. Other than edge cases, the ony concern of a Controller is to serve inbound HTTP requests and dispatch to the right Services.
public class CustomerService : Service<Customer>, ICustomerService
{
    private readonly ITrackableRepository<Order> _ordeRepository;

    public CustomerService(
        ITrackableRepository<Customer> customerRepository,
        ITrackableRepository<Order> ordeRepository) : base(customerRepository)
    {
        _ordeRepository = ordeRepository;
    }

    public async Task<IEnumerable<Customer>> CustomersByCompany(string companyName)
    {
        return await Repository
            .Queryable()
            .Where(x => x.CompanyName.Contains(companyName))
            .ToListAsync();
    }

    public async Task<decimal> CustomerOrderTotalByYear(string customerId, int year)
    {
        return await Repository
            .Queryable()
            .Where(c => c.CustomerId == customerId)
            .SelectMany(c => c.Orders.Where(o => o.OrderDate != null && o.OrderDate.Value.Year == year))
            .SelectMany(c => c.OrderDetails)
            .Select(c => c.Quantity * c.UnitPrice)
            .SumAsync();
    }

    public async Task<IEnumerable<CustomerOrder>> GetCustomerOrder(string country)
    {
        var customers = Repository.Queryable();
        var orders = _ordeRepository.Queryable();

        var query = from c in customers
            join o in orders on new { a = c.CustomerId, b = c.Country }
                equals new { a = o.CustomerId, b = country }
            select new CustomerOrder
            {
                CustomerId = c.CustomerId,
                ContactName = c.ContactName,
                OrderId = o.OrderId,
                OrderDate = o.OrderDate
            };

        return await query.ToListAsync();
    }
}

Kendo UI Grid Service w/ Asp.Net.Core.OData (OData v4.x)

Northwind.Web\src\app\services\edit.service.ts

  • Reusable Kendo UI Grid Service, this service's concern is to handle most developer use cases reguarding the Grid e.g. Add, Updating, Deleting, and fetching data, as well as the Grid's state management.
  • Change tracking
    • New Items
    • Deleted Items
    • Updated Items
    • Undo, Rollback, Cancel Changes and restore to previous original state
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { toODataString, State } from '@progress/kendo-data-query';
import { environment } from '../../environments/environment';
import { GridDataResult, DataStateChangeEvent } from '@progress/kendo-angular-grid';
import 'rxjs/add/operator/zip';

const cloneData = ( data ) => data.map( item => Object.assign( {}, item ) );

export abstract class EditService extends BehaviorSubject<GridDataResult> {
  private data = new DataResult();
  private originalData = new DataResult();
  private createdItems: any[] = [];
  private updatedItems: any[] = [];
  private deletedItems: any[] = [];
  private errors: any[];
  public state: State;
  private baseUrl = `${ environment.apiUrl }`;
  private url = `${ this.baseUrl }${ this.resource }`;
  private queryString = '';
  public loading = true;

  constructor (
    private http: HttpClient
    , private resource: string
    , private keys: Array<string>
  ) { super( null ); }

  public read ( queryString = '' ) {

    this.loading = true;

    if (queryString)
      this.queryString = queryString;

    this.fetch()
      .do( data => { this.data = new DataResult( cloneData( data.value ), data.total ); } )
      .do( data => this.originalData = new DataResult( cloneData( data.value ), data.total ) )
      .finally( () => this.loading = false )
      .subscribe( data => { super.next( data ); } );
  }

  public create ( item: any ): void {
    this.createdItems.push( item );
    this.data.unshift( item );
    super.next( this.data );
  }

  public update ( item: any ): void {
    if ( !this.isNew( item ) ) {
      const index = this.itemIndex( item, this.updatedItems );
      if ( index !== -1 )
        this.updatedItems.splice( index, 1, item );
      else
        this.updatedItems.push( item );
    } else {
      const index = this.itemIndex( item, this.createdItems );
      this.createdItems.splice( index, 1, item );
    }
  }

  public remove ( item: any ): void {
    let index = this.itemIndex( item, this.data.value );
    this.data.splice( index, 1 );

    index = this.itemIndex( item, this.createdItems );
    if ( index >= 0 )
      this.createdItems.splice( index, 1 );
    else
      this.deletedItems.push( item );

    index = this.itemIndex( item, this.updatedItems );
    if ( index >= 0 )
      this.updatedItems.splice( index, 1 );

    super.next( this.data );
  }

  public isNew ( item: any ): boolean {
    return this.keys.every( x => !item[ x ] );
  }

  public hasChanges (): boolean {
    return Boolean( this.deletedItems.length || this.updatedItems.length || this.createdItems.length );
  }

  public hasItems (): boolean {
    return Boolean( this.data.length );
  }

  public saveChanges (): void {
    if ( !this.hasChanges() ) return;

    const completed = [];

    this.deletedItems.forEach( item => {
      let uri = `${ this.url }(${ item[ this.keys[ 0 ] ] })`; // e.g. /odata/Orders(3)

      if ( this.keys.length > 1 )
        uri = `${ this.url }(${ this.keys.map( key => `${ item[ key ] }` ).join( '&' ) })`; // e.g. /odata/Orders(CustomerId=3,OrderId=7)

      completed.push( this.http.delete( uri ) );
    } );

    this.updatedItems.forEach( item => {
      let uri = `${ this.url }(${ this.keys.map( key => `${ item[ key ] }` ).join( '&' ) })`; // e.g. /odata/Orders(3)

      if ( this.keys.length > 1 )
        uri = `${ this.url }(${ this.keys.map( key => `${ key }=${ item[ key ] }` ).join( ',' ) })`; // e.g. /odata/Orders(CustomerId=3,OrderId=7)

      completed.push( this.http.patch( uri, item ) );
    } );

    this.createdItems.forEach( item => {
      const uri = `${ this.url }`; // e.g. /odata/Orders
      completed.push( this.http.post( uri, item ) );
    } );

    this.reset();

    Observable.zip( ...completed ).subscribe( () => this.read( this.queryString ) );
  }

  public cancelChanges (): void {
    this.reset();
    this.data = this.originalData;
    this.originalData = new DataResult( cloneData( this.originalData.value ), this.originalData.total );
    super.next( this.data );
  }

  public assignValues ( target: any, source: any ): void {
    Object.assign( target, source );
  }

  private reset () {
    this.data = new DataResult();
    this.deletedItems = [];
    this.updatedItems = [];
    this.createdItems = [];
  }

  public onStateChange ( state: DataStateChangeEvent ) {
    this.state = state;
    this.read(this.queryString);
  }

  private fetch (): Observable<DataResult> {
    const queryStr = `${ toODataString( this.state ) }&$count=true${ this.queryString }`;
    return this.http
      .get( `${ this.url }?${ queryStr }` )
      .map( ( response ) => {
        const data = ( response as any ).value;
        const total = parseInt( response[ '@odata.count' ], 10 );
        return new DataResult( data, total );
      } );
  }

  itemIndex = ( item: any, data: any[] ): number => {
    for ( let idx = 0; idx < data.length; idx++ ) {
      if ( this.keys.every( key => data[ idx ][ key ] === item[ key ] ) ) {
        return idx;
      }
    }
    return -1;
  }

}

// https://en.wikipedia.org/wiki/Adapter_pattern
class DataResult implements GridDataResult {
  data = [];
  total = 0;

  constructor ( data?: any[], total?: number ) {
    this.data = data || [];
    this.total = total || 0;
  }
  unshift = ( item ) => {
    this.data.unshift( item ); this.total++;
  }
  splice = ( index, item ) => {
    this.data.splice( index, item ); this.total--;
  }
  get length () { return this.data.length; }
  map = ( x ) => this.data.map( x );
  get value () { return this.data; }
}

Northwind.Web\src\app\app.component.html

<kendo-grid 
  [kendoGridInCellEditing]="createFormGroup" 
  [editService]="productGridService" 
  [data]="productGridService | async" 
  [pageSize]="productGridService.state.take" 
  [skip]="productGridService.state.skip" 
  [sort]="productGridService.state.sort" 
  [pageable]="true" 
  [sortable]="true" 
  (dataStateChange)="productGridService.onStateChange($event)">
  <ng-template kendoGridToolbarTemplate>
    <button kendoGridAddCommand>Add new</button>
    <button class='k-button' [disabled]="!productGridService.hasChanges()" (click)="productGridService.saveChanges();">Save Changes</button>
    <button class='k-button' [disabled]="!productGridService.hasChanges()" (click)="productGridService.cancelChanges();">Cancel Changes</button>
  </ng-template>
  <kendo-grid-column field="ProductId" title="Id" [editable]="false"></kendo-grid-column>
  <kendo-grid-column field="ProductName" title="Product Name"></kendo-grid-column>
  <kendo-grid-column field="UnitPrice" editor="numeric" title="Price"></kendo-grid-column>
  <kendo-grid-column field="Discontinued" editor="boolean" title="Discontinued"></kendo-grid-column>
  <kendo-grid-column field="UnitsInStock" editor="numeric" title="Units In Stock"></kendo-grid-column>
  <kendo-grid-command-column title="" width="220">
    <ng-template kendoGridCellTemplate let-isNew="isNew">
      <button kendoGridRemoveCommand>Remove</button>
      <button kendoGridSaveCommand [disabled]="formGroup?.invalid">Add</button>
      <button kendoGridCancelCommand>Discard</button>
    </ng-template>
  </kendo-grid-command-column>
</kendo-grid>

Northwind.Web\src\app\services\product-grid.service.ts

  • Setup or override default Grid State properties e.g. paging, sorting, filtering, etc.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { EditService } from './edit.service';

@Injectable()
export class ProductGridService extends EditService {

  constructor (http: HttpClient) {
    super( http, 'Products', [ 'ProductId' ] );
    this.state = {
      sort: [],
      skip: 0,
      take: 10
    };
  }
}

Northwind.Web\src\app\app.component.ts

  • Grid implementation & heavy lifting is handled by ProductGridService which extends EditService
  • Component/ViewModel is light-weight and clean, due to resuable EditService for any Grid heavy-lifting
@Component( {
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.scss' ]
} )
export class AppComponent implements OnInit {
  public formGroup: FormGroup;
  public changes: any = {};

  constructor (
    public formBuilder: FormBuilder
    , public productGridService: ProductGridService ) {
    this.createFormGroup = this.createFormGroup.bind( this );
  }

  public ngOnInit (): void {
    this.productGridService.read();
  }

  public createFormGroup ( args: any ): FormGroup {
    const item = args.isNew ? new Product() : args.dataItem;

    this.formGroup = this.formBuilder.group( {
      'ProductId': item.ProductId,
      'ProductName': [ item.ProductName, Validators.required ],
      'UnitPrice': item.UnitPrice,
      'UnitsInStock': [ item.UnitsInStock, Validators.required ],
      'Discontinued': item.Discontinued
    } );

    return this.formGroup;
  }
}

URF.Core Sample Angular & Hosting w/ Node.js in Azure (goo.gl/QRps9g)

var express = require('express')
  , http = require('http')
  , path = require('path');

var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('*', (req, res) => {
  res.sendFile(`index.html`, { root: 'public' });
});

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

Please see https://github.com/urfnet/URF.Core.Sample/tree/master/Northwind.Web/server, for directory contents and structure for more details for hosting Angular w/ Node.js in Microsoft Azure.

© 2018 URF.NET All rights reserved.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].