Giter Site home page Giter Site logo

urfnet / urf.core.sample Goto Github PK

View Code? Open in Web Editor NEW
45.0 17.0 16.0 3.99 MB

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

Home Page: https://goo.gl/WwtQ5h

C# 18.18% TypeScript 5.09% JavaScript 0.82% HTML 5.38% CSS 0.13% Rich Text Format 70.40%
urf asp-net-core kendo-ui angular odata design-patterns service repository unit-of-work entity-framework-core

urf.core.sample's Introduction

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.

urf.core.sample's People

Contributors

lelong37 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

Watchers

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

urf.core.sample's Issues

Microsoft.AspNetCore.OData

Hi, first of all thank you very much for this work. I apprecite very much.

I have downloaded this sample solution and opened with visual studio community (version 15.5.7) but i have got this error:

Errore NU1102 Il pacchetto Microsoft.AspNetCore.OData con versione (>= 7.0.0-Nightly201802141312) non è stato trovato

  • Trovata/e 9 versione/i in nuget.org [ Versione più vicina: 7.0.0-beta1 ]
  • Trovata/e 0 versione/i in Microsoft Visual Studio Offline Packages Northwind.Api C:\Users\ugo\Documents\Visual Studio 2017\Projects\URF.Core.Sample-master\Northwind.Api\Northwind.Api.csproj 1

Have I to uninstall Microsoft.AspNetCore.OData 7.0.0-Nightly201802141312 and install the available version 7.0.0-beta1 ?

Thanks very much.
best regards.
Ugo Montefiori

DTO for data transfer.

How to use DTO to transfer data between Service Layer and UI Layer or Web Api.
drawing1 4 this picture explain my question. In addition, POCO entities must be separated from the presentation layer. The presentation layer can also be used in view models. What is the best way to do that?

Upgrading Microsoft.AspNetCore, Microsoft.VisualStudio.Web packages brakes the app.

Upgrading Microsoft.AspNetCore, Microsoft.VisualStudio.Web packages brakes the app.

-    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0-preview1-final" />
-    <PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.1.0-preview1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.1.0-rc1-final" />
-    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.0-preview1-final" />
-    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGenerators.Mvc" Version="2.1.0-preview2-final" />
+    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGenerators.Mvc" Version="2.1.0-rc1-final" />

Output

HTTP Error 502.5 - Process Failure

Common causes of this issue:
The application process failed to start
The application process started but then stopped
The application process started but failed to listen on the configured port

Troubleshooting steps:
Check the system event log for error messages
Enable logging the application process' stdout messages
Attach a debugger to the application process and inspect

For more information visit: https://go.microsoft.com/fwlink/?LinkID=808681

Unit/Integration Test Setup Samples

Apologies if this is not the appropriate place for this request but would it be possible to add unit/integration test examples to this project?

I've used older versions of URF on full .Net using Unity as the IOC container. Given ASP.NET Core has it's own IOC container setup I would like to do things the idiomatic way but I'm struggling to find an example of how I would test controllers and services that use URF.Core with the built in IOC container.

Below is a sample from an older project of how I would unit/integration test with unity. Looking for how to translate this into .net core

## UnityConfig.cs

            ....
            //--IT Assets
            .RegisterType<ISoftwareService, SoftwareService>()
            .RegisterType<IRepositoryAsync<Software>, Repository<Software>>()

## SoftwareService_SUTBuilder.cs

public class SoftwareService_SUTBuilder
   {
       private List<Software> _software;
       private List<SoftwareLicense> _licenses;
       private List<SoftwareWarranty> _warranties;

       private SUTBuilder_ActorHelper _actorHelper;

       private IUnityContainer _container;
       private static Random random = new Random();

       public SoftwareService_SUTBuilder()
       {
           _container = new UnityContainer();
           UnityConfig.RegisterTypes(_container);

           _actorHelper = new SUTBuilder_ActorHelper(_container);

           _software = new List<Software>();
           _licenses = new List<SoftwareLicense>();
           _warranties = new List<SoftwareWarranty>();
       }

       public SoftwareService_SUTBuilder AddSoftware(Software item)
       {
           if (item != null)
               _software.Add(item);
           return this;
       }

       public SoftwareService_SUTBuilder AddSoftware(string name, decimal cost = 0, string version = null)
       {
           ....
           return this;
       }

       public SoftwareService_SUTBuilder WithDefaults(int size = 10)
       {
          ....
           return this;
       }

       internal Software GetSoftware(string name)
       {
           var software = _software.FirstOrDefault(x => x.Name == name);
           return software;
       }

       private string GenerateRandomKey(int length = 12)
       {
           ....
       }

       public SoftwareService Build()
       {
           _actorHelper.BuildActors();

           var repo = _container.Resolve<IRepositoryAsync<Software>>();
           if (SymphonyUtils.IsNonEmptyList(_software))
           {
               repo.InsertRange(_software);
           }

           if (SymphonyUtils.IsNonEmptyList(_licenses))
           {
               var licenseRepo = _container.Resolve<IRepositoryAsync<SoftwareLicense>>();
               licenseRepo.InsertRange(_licenses);
           }

           if (SymphonyUtils.IsNonEmptyList(_warranties))
           {
               var warrantyRepo = _container.Resolve<IRepositoryAsync<SoftwareWarranty>>();
               warrantyRepo.InsertRange(_warranties);
           }

           var securityService = _container.Resolve<ISecurityService>();
           var changeRecordService = _container.Resolve<IChangeRecordService>();
           var inventoryService = _container.Resolve<IInventoryItemService>();
           var log = _container.Resolve<ILogger>();

           var service = new SoftwareService(repo, securityService, inventoryService, changeRecordService, log);
           return service;
       }
   }

## SoftwareService_Tests.cs

        [Fact]
        [Trait("Type", "IT")]
        [Trait("Type", "Software")]
        public void SoftwareServiceShould_WhenGetById_ReturnSoftware()
        {
            //Arrange
            var builder = new SoftwareService_SUTBuilder()
                                    .AddSoftware("Visual Studio", 600, "2017.01")
                                    .AddSoftware("Visual Studio Code", 0, "May 2017")
                                    .AddSoftware("Chrome");
            var service = builder.Build();
            var targetSoftware = builder.GetSoftware("Chrome");

            //Act
            var software = service.GetById(targetSoftware.Id);

            //Assert
            Assert.NotNull(software);
            Assert.Equal(targetSoftware.Name, software.Name);
        }

Thank you!!

Transactional integrity using oData Batch

I'm trying to use odata's batch functions with net core 2.1.

on API:
...
app.UseODataBatching ();
routeBuilder.MapODataServiceRoute ("ODataRoute", "odata", oDataConventionModelBuilder.GetEdmModel (), new DefaultODataBatchHandler ());

on Client:
...
var responses = await container.SaveChangesAsync (SaveChangesOptions.BatchWithSingleChangeset);

The container calls the various API controllers but the transactional integrity is not managed and if an update goes wrong it does not roll back.

Should UnitOfWork not be responsible for completing the transaction?
Do I have to set some parameters in the api project setup?
Thank you
Ugo

AddDbContext was called with configuration, but the context type 'ContextClass' only declares a parameterless constructor?

Detail

I am trying to create asp.net core application and use scaffold-dbcontext to generate class. when I run this app it shows me the error. I already follow some answer on stackoverflow with the same issue but mine remains.

Error

AddDbContext was called with configuration, but the context type 'MyContext' only declares a parameterless constructor. This means that the configuration passed to AddDbContext will never be used. If configuration is passed to AddDbContext, then 'puffinDBContext' should declare a constructor that accepts a DbContextOptions and must pass it to the base constructor for DbContext.

`public partial class MyContext: DbContext
{

public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}


public virtual DbSet<Answer> Answer { get; set; }
public virtual DbSet<Attachment> Attachment { get; set; }
public virtual DbSet<Category> Category { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

    if (!optionsBuilder.IsConfigured)
    {
        optionsBuilder.UseSqlServer(@"Server=.;Database=MyContext;Trusted_Connection=True;");
    }
protected override void OnModelCreating(ModelBuilder modelBuilder) { .... }

}`

Startup Class

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

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

        services.AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    }

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.