2018年3月18日 星期日

範例:ASP.NET Core 2.0 DI & EntityFrameworkCore & Repository Pattern


假設建立了一個 DbContext 叫做 TempDbContext

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

        }

       
public DbSet<Product> Products { get; set; }
    }

IRepository<T為一些常用操作(例如CRUD)的 interface
IProductRepository 為操作 Product 的 interface,並包含了IRepository<T>

   
public interface IRepository<T> where T : class
    {
       
IEnumerable<T> GetAll();
       
IEnumerable<T> Find(Func<T, bool> predicate);
       
T GetById(int id);
       
void Create(T entity);
       
void Update(T entity);
       
void Detele(T entity);
       
int Count(Func<T, bool> predicate);
    }

   
public interface IProductRepository : IRepository<Product>
    {
        //
一些操作 Product 的方法
       
Product GetProductByName(string name);
    }

實作 IRepository<T>

    public class Repository<T> : IRepository<T> where T : class
    {
       
// 會利用 ASP.NET Core 2.0 DI 注入實例
        protected readonly TempDbContext _context;
       
public Repository(TempDbContext context)
        {
            _context = context;
        }
       
public void Save() => _context.SaveChanges();

       
public int Count(Func<T, bool> predicate)
        {
           
return _context.Set<T>().Where(predicate).Count();
        }

       
public void Create(T entity)
        {
            _context.Add(entity);
            Save();
        }

       
public void Detele(T entity)
        {
            _context.Remove(entity);
            Save();
        }

       
public IEnumerable<T> Find(Func<T, bool> predicate)
        {
           
return _context.Set<T>().Where(predicate);
        }

       
public IEnumerable<T> GetAll()
        {
           
return _context.Set<T>();
        }

       
public T GetById(int id)
        {
           
return _context.Set<T>().Find(id);
        }

       
public void Update(T entity)
        {
            _context.Entry(entity).State =
EntityState.Modified;
            Save();
        }
    }

實作 IProductRepository,也繼承 Repository<Product>

    public class ProductRepository : Repository<Product>, IProductRepository
    {
       
public ProductRepository(TempDbContext context) : base(context)
        {
        }

       
public Product GetProductByName(string name)
        {
           
return _context
                .Products
                .Where(p => p.Name == name)
                .FirstOrDefault();
        }
    }

ASP.NET Core 2.0 DI 設定(Startup)

        public void ConfigureServices(IServiceCollection services)
        {
           
// Repository 建構子需要 DbContext
            services.AddDbContext<TempDbContext>(options => options.UseInMemoryDatabase("TempContext"));
            services.AddTransient<
IProductRepository, ProductRepository>();
            services.AddMvc();
        }

Controller 中使用 Repository

    [Route("api/[controller]")]
   
public class ProductController : Controller
    {
       
private readonly IProductRepository _productRepository;

       
public ProductController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
    }

==========
Debug:
在執行時發生了這個錯誤

Additional information: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
確定在ConfigureServices有設定DbContext的注入,後來發現是TempDbContext的建構子忘了呼叫base(options)

2018年3月15日 星期四

快速建立一個完整的React Application專案 (有路由 & 可呼叫後端API & 開發階段用webpack server)


整合以下連結內的教學建立一個專案:
1.[Webpack 4 Tutorial: from 0 Conf to Production Mode]
2.[React Redux Tutorial for Beginners: learning Redux in 2018]
3.[React Router v4 官方文件: Quick Start ]
4.[Redux Async Actions - Redux Tutorial #6]

安裝:
$ npm install --save-dev webpack webpack-cli webpack-dev-server babel-core babel-loader babel-preset-env babel-preset-react babel-plugin-transform-object-rest-spread html-webpack-plugin html-loader
$ npm install --save react react-dom react-router-dom prop-types redux react-redux redux-thunk redux-promise-middleware redux-logger axios

配置:
webpack.config.js
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
    devtool: 'inline-source-map',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            },
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'html-loader',
                        options: { minimize: true }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebPackPlugin({
            template: './src/index.html',
            filename: './index.html'
        })
    ]
};

.babelrc
{
    "presets": ["env", "react"],
    "plugins": ["transform-object-rest-spread"]
}

package.jsonscript
    "scripts": {
        "start": "webpack-dev-server --mode development --open",
        "build": "webpack --mode production"
    },

專案結構:
[src]
          [js]
                   [actions]
                   [components]
                   [constants]
                   [reducers]
                   [store]
          index.js
          index.html

備註:
想關掉webpack server時按下Ctrl+C,但是還是在running,要真的停止必須執行:
taskkill /F /IM node.exe

Redux 的   Middleware 套件說明:
1.      reduxdispatch只接受plain object,可使用redux-thunk來讓dispatch也可以接受function
2.      使用redux-promise-middleware可以讓action.payloadPromise類型來達到簡化設計三種狀態:Penging, Fulfilled, Rejected
3.      redux-promise-middleware可以和redux-thunk結合起來鍊接action
範例:使用者登入後取得他的文章
const mapDispatchToProps = dispatch => {
    return {
        login: (email, password) => dispatch(login(email, password))
    };
};

export const login = (email, password) => dispatch => {
    dispatch({
        type: 'LOGIN',
        payload: axios.post('http://localhost:5000/api/Token', {
            email,
            password
        })
    }).then(reponse => {
        dispatch({
            type: 'FETCH_POSTS',
            payload: axios.get('http://localhost:5000/api/posts')
        });
    });
};