2020年11月20日 星期五

2020年11月13日 星期五

不同使用者共用物件並同步雙向更新

建立共用類別
public class Filter
    {
        private string _filter;
        public string filter
        {
            get
            {
                return _filter;
            }
            set
            {
                _filter = value;
                NotifyDataChanged();
            }
        }
        public event Action OnChange;
        private void NotifyDataChanged() => OnChange?.Invoke();
    }

startup.cs 註冊為服務
public void ConfigureServices(IServiceCollection services)
        {           
            services.AddSingleton<Filter>();
        }

.razor 
====
注入服務
@inject demo1.Filter Filter

顯示於畫面
<input type="text" @bind="Filter.filter" @bind:event="oninput">

設定物件內容改變時通知以便刷新頁面
protected override void OnInitialized()
    {
        Filter.OnChange+= OnMyChangeHandler;
    }
 public void Dispose()
    {
        Filter.OnChange -= OnMyChangeHandler;
    }
    private async void OnMyChangeHandler()
    {
        await InvokeAsync(StateHasChanged);
    }

透過 Entity Framework Core 工具產生資料庫表格相關類別

開啟套件管理器主控台

安裝工具 (亦可透過 nuget)
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.SqlServer

產生檔案
Scaffold-DbContext Name=xxx Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models\xxx -Tables xxx,xxx -force

appsettings.json 建立連線字串
 "ConnectionStrings": {
    "xxx": "Server=xxx; Database=xxx; User Id=xxx; Password=xxx;"  
  }

Blazor 學習心得

實作篩選方塊

html
<input type="text" @bind="filter"   @bind:event="oninput"> <= 若要隨時輸入資料立刻篩選則改變預設事件onchange 為 oninput,否則失去焦點才會改變 filter 
@foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }

c#
private IEnumerable<WeatherForecast> forecasts => forecastss.Where(a => filter!=default && a.Summary.Contains( filter) || filter == default || filter == "");

呼叫 javascript 函數

html
@inject IJSRuntime JS;
<script>
        window.Alert = function (message) {
            alert(message);
        }
</script>

c#
await JS.InvokeVoidAsync("Alert", "123");

存取資料庫

startup.cs
public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextFactory<xxxContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("xxx")));

xxxContext.cs
刪除空參數建構者避免出錯

設定元件
@implements IDisposable
@inject Microsoft.EntityFrameworkCore.IDbContextFactory<xxxContext> DbFactory

產生DbContext
xxxContext db;
protected override async Task OnInitializedAsync()
    {
        db = DbFactory.CreateDbContext();
        await base.OnInitializedAsync();
    }

釋放DbContext
public void Dispose()
    {
        db.Dispose();
    }


2020年11月5日 星期四

透過 Entity Framework Core 命令列介面產生資料庫表格相關類別 (for Visual Studio Code)

安裝工具
dotnet tool install --global dotnet-ef

更新工具
dotnet tool update --global dotnet-ef

安裝套件
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer

建立工作
{
            "label": "更新資料庫",
            "type": "process",
            "command": "dotnet",
            "args": [
                "ef",
                "-p",
                "${workspaceFolder}\\xxx.csproj",
                "dbcontext",
                "scaffold",
                "Name=[appsettings.json資料庫連線名稱]",
                "Microsoft.EntityFrameworkCore.SqlServer",
                "-o",
                "${workspaceFolder}\\xxx\\Models\\[用資料庫名稱區隔不同的資料夾避免名稱衝突]",
                "-t",
                "xxx",                 
                "-f"
            ],
            "problemMatcher": "$msCompile"
}

Visual Studio Code 發佈方式

透過工作命令

加入工作
{
                "label": "發行",
                "type": "process",
                "command": "發佈.bat",
                "args": [],
}

發佈.bat
dotnet build xxx\xxx.csproj -c Release -o xxx
copy app_offline.htm xxx
dotnet publish xxx\xxx.csproj -c Release -o xxx --no-build
del xxx\app_offline.htm
※app_offline.htm 會造成app pool 停止,但這樣複製檔案才能避免鎖定問題

執行工作 : 終端機 > 執行工作

2020年11月4日 星期三

ASP.NET Core MVC 資料庫連線設定與使用方式

資料庫連線設定與使用方式
appsettings.json 設定連線字串
"ConnectionStrings": {
"xxx": "Server=xxx; Database=xxx; User Id=xxx; Password=xxx;"
}
startup.cs 加入連線
public void ConfigureServices (IServiceCollection services) {
services.AddDbContext<xxxContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("xxx")));
xxxController.cs 使用連線
public class xxxController : ControllerBase {
private readonly xxxContext db;
public xxxController(xxxContext db)
{
this.db = db;
}

若要單獨使用不透過controller,則須擴展xxxContext.cs
public partial class xxxContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                IConfigurationRoot configuration = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                .AddJsonFile("appsettings.json")
                .Build();
                optionsBuilder.UseSqlServer(configuration.GetConnectionString("xxx"));
            }
        }
使用:
using (var db=new xxxContext()) {
...
}

※存取關聯table 要先include,否則產生集合(.ToList())後關聯物件都會是null
db.aaa.Where(a =>...).Include(a=>a.xxx) // 事後才能取得 a.xxx.ooo
※ 使用 .GroupBy 前必須先 .AsEnumerable 否則可能會報錯
※ 使用內建連線,action 回傳型態不能為 void,否則會造成 SaveChangesAsync() 出錯,因為會提前dispose(不會等待執行)

.net 7 避免 sql server table 有trigger 造成 table 無法更新 (有trigger 會改用舊架構,非必要不要使用)

擴展xxxContext.cs
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
        {
            configurationBuilder.Conventions.Add(_ => new BlankTriggerAddingConvention());
        }
public class BlankTriggerAddingConvention : IModelFinalizingConvention
    {
        public virtual void ProcessModelFinalizing(
            IConventionModelBuilder modelBuilder,
            IConventionContext<IConventionModelBuilder> context)
        {
            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
            {
                var table = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table);
                if (table != null
                    && entityType.GetDeclaredTriggers().All(t => t.GetDatabaseName(table.Value) == null))
                {
                    entityType.Builder.HasTrigger(table.Value.Name + "_Trigger");
                }

                foreach (var fragment in entityType.GetMappingFragments(StoreObjectType.Table))
                {
                    if (entityType.GetDeclaredTriggers().All(t => t.GetDatabaseName(fragment.StoreObject) == null))
                    {
                        entityType.Builder.HasTrigger(fragment.StoreObject.Name + "_Trigger");
                    }
                }
            }
        }
    }

自動延遲載入

安裝套件 Microsoft.EntityFrameworkCore.Proxies
startup.cs 
services.AddDbContext<xxx>(options =>
 options.UseSqlServer(Configuration.GetConnectionString("圓桌基本資料")).UseLazyLoadingProxies());       
擴展xxxContext.cs
optionsBuilder.UseSqlServer(connString).UseLazyLoadingProxies();
※ 於 .net 6 似乎會造成 EF 無法使用,待確認

2020年11月3日 星期二

Angular 設定容器高度為可用剩餘高度

使用 attribute directive

建立 class : ng generate directive bodyinner --module app
constructor(private el: ElementRef) {
}
ngAfterViewInit() {
    this.el.nativeElement.style.height = (window.innerHeight - this.el.nativeElement.offsetTop) + 'px';
}

html:
<div appBodyinner style="overflow-y:auto"></div>


input 連結 datalist 用程式控制彈出選項

範例: nextTick(() => document.querySelector('input').showPicker());  ※僅支援現代瀏覽器