场景:
在一个工厂ERP管理系统中,有部分实体的界面需要显示相关联的其它信息,而这部分信息是弱连接的,需要调用WebAPI取回,传统的方式是一一取回,结果是用户感知的速度达不到要求。
使用BackgroundWorker,在后台取资料,取完资料后再显示,如果取资料的过程中,主实体已经变更了,或者已多次变更了,则等上次任务完成后,直接再取最后一次变更的资料,中间的直接丢弃,既加快了速度,也节省了资源。
实现思路:

先加入BackgroundWorker,(命名如:BackgroundWorker_ShowItem)。

再定义两个变量,用于保存要显示的主实体(或其关联键)

private T Item = null;
private T NextItem = null;

当需要显示一个主体的内容时,不直接显示,而是呼叫BackgroundWorker来处理

public void ShowItem(T item)
{
    if (BackgroundWorker_ShowItem.IsBusy)
    {
    this.NextItem = item;
    }
    else
    {
    this.Item = item;
    this.BackgroundWorker_ShowItem.RunWorkerAsync();
    }
}

private void BackgroundWorker_ShowItem_DoWork(object sender, DoWorkEventArgs e)
{
    this.ShowItem();
}

private void BackgroundWorker_ShowItem_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (this.NextItem != null)
    {
    var nextItem = this.NextItem;
    this.NextItem = null;
    this.ShowItem(nextItem);
    }
}

注意,当ShowItem()中有访问前端时,会报“线程间操作无效”的例外,需要使用Invoke,但不要在DoWork中将ShowItem()全部包含,而应该在ShowItem()中单独处理。否则达不到性能优化的效果。

在C#中,implicit operatorexplicit operator 都用于用户定义的类型转换,但它们在转换方式和安全性上有重要区别。

1. implicit operator(隐式转换)

特点:

  • 自动进行,不需要显式类型转换
  • 转换是安全的,不会丢失数据或引发异常
  • 编译器自动识别并执行转换

示例代码:

public class Celsius
{
    public double Temperature { get; set; }
    
    public Celsius(double temp)
    {
        Temperature = temp;
    }
    
    // 隐式转换:double → Celsius
    public static implicit operator Celsius(double d)
    {
        return new Celsius(d);
    }
    
    // 隐式转换:Celsius → double
    public static implicit operator double(Celsius c)
    {
        return c.Temperature;
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        // 隐式转换 - 自动进行
        Celsius c = 25.5;           // double 自动转为 Celsius
        double temp = c;            // Celsius 自动转为 double
        
        Console.WriteLine($"温度: {temp}°C"); // 输出: 温度: 25.5°C
        
        // 在方法调用中也自动转换
        DisplayTemperature(30.0);   // double 自动转为 Celsius
    }
    
    static void DisplayTemperature(Celsius celsius)
    {
        Console.WriteLine($"显示温度: {celsius.Temperature}°C");
    }
}

2. explicit operator(显式转换)

特点:

  • 必须显式指定类型转换
  • 可能丢失数据或引发异常
  • 需要程序员明确意图

示例代码:

public class Money
{
    public decimal Amount { get; set; }
    public string Currency { get; set; }
    
    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
    
    // 显式转换:Money → decimal(可能丢失货币信息)
    public static explicit operator decimal(Money money)
    {
        return money.Amount;
    }
    
    // 显式转换:decimal → Money(需要指定默认货币)
    public static explicit operator Money(decimal amount)
    {
        return new Money(amount, "USD");
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        Money salary = new Money(5000.00m, "USD");
        
        // 显式转换 - 必须明确指定
        decimal amount = (decimal)salary;        // Money → decimal
        Money money = (Money)2500.00m;           // decimal → Money
        
        Console.WriteLine($"金额: {amount}");    // 输出: 金额: 5000.00
        Console.WriteLine($"货币: {money.Currency}, 金额: {money.Amount}");
        
        // 如果不使用显式转换,编译器会报错
        // decimal wrong = salary; // 错误: 无法隐式转换
    }
}

3. 综合对比示例

public class Distance
{
    public double Meters { get; set; }
    
    public Distance(double meters)
    {
        Meters = meters;
    }
    
    // 隐式转换:int → Distance(总是安全的)
    public static implicit operator Distance(int meters)
    {
        return new Distance(meters);
    }
    
    // 显式转换:Distance → int(可能丢失精度)
    public static explicit operator int(Distance d)
    {
        return (int)d.Meters;
    }
    
    // 隐式转换:double → Distance(总是安全的)
    public static implicit operator Distance(double meters)
    {
        return new Distance(meters);
    }
    
    // 显式转换:Distance → double(安全,但为了对称性使用显式)
    public static explicit operator double(Distance d)
    {
        return d.Meters;
    }
    
    public override string ToString()
    {
        return $"{Meters}米";
    }
}

class Program
{
    static void Main()
    {
        // 隐式转换示例
        Distance d1 = 100;          // int → Distance (隐式)
        Distance d2 = 123.45;       // double → Distance (隐式)
        
        Console.WriteLine($"d1: {d1}"); // 输出: d1: 100米
        Console.WriteLine($"d2: {d2}"); // 输出: d2: 123.45米
        
        // 显式转换示例
        int metersInt = (int)d2;    // Distance → int (显式,丢失精度)
        double metersDouble = (double)d2; // Distance → double (显式)
        
        Console.WriteLine($"整数米数: {metersInt}");   // 输出: 整数米数: 123
        Console.WriteLine($"精确米数: {metersDouble}"); // 输出: 精确米数: 123.45
        
        // 在运算中的使用
        Distance total = d1 + 50;   // 隐式转换 50 → Distance
        Console.WriteLine($"总距离: {total}"); // 输出: 总距离: 150米
    }
}

4. 关键区别总结

特性implicit operatorexplicit operator
转换方式自动必须显式指定
安全性安全,不会丢失数据可能不安全,可能丢失数据
使用场景无损转换、小范围到大范围有损转换、大范围到小范围
编译器行为自动识别转换需要强制类型转换语法
代码可读性更简洁更明确意图

5. 最佳实践建议

  1. 使用 implicit operator 当:

    • 转换100%安全
    • 不会丢失信息
    • 转换是直观的
  2. 使用 explicit operator 当:

    • 可能丢失数据或精度
    • 转换可能失败
    • 需要用户明确转换意图
  3. 避免过度使用隐式转换,以免降低代码可读性。

这种机制让C#能够提供灵活的类型转换,同时保持类型安全性和代码清晰度。

原因在于每个扇区的物理字节数。

使用以下命令可以查看:

fsutil fsinfo sectorinfo c:
LogicalBytesPerSector :                                 512
PhysicalBytesPerSectorForAtomicity :                    32768
PhysicalBytesPerSectorForPerformance :                  32768
FileSystemEffectivePhysicalBytesPerSectorForAtomicity : 4096
设备校准 :                                        已校准(0x000)
设备上的分区校准:                                  已校准(0x000)
无搜寻惩罚
支持剪裁
不支持 DAX
未精简预配

这两个32768是导致sqlserver出错的原因。
解决方案就是执行以下命令:

reg add "HKLM\SYSTEM\CurrentControlSet\Services\stornvme\Parameters\Device" /v "ForcedPhysicalSectorSizeInBytes" /t reg_multi_sz /d "* 4095" /f

重启之后再查看结果:

LogicalBytesPerSector :                                 512
PhysicalBytesPerSectorForAtomicity :                    4096
PhysicalBytesPerSectorForPerformance :                  4096
FileSystemEffectivePhysicalBytesPerSectorForAtomicity : 4096
设备校准 :                                        已校准(0x000)
设备上的分区校准:                                  已校准(0x000)
无搜寻惩罚
支持剪裁
不支持 DAX
未精简预配

详情

取消默认可查看任何数据库

DENY VIEW any DATABASE TO PUBLIC;

赋予自己拥有管理权限的数据库的查看权限

ALTER AUTHORIZATION ON DATABASE::[Database] TO [User];

注意:
如果数据库已存在用户权限,则可能要删除后再操作

如果需要该用户可以使用SQL Profiler来跟踪SQL,则需要执行:

GRANT ALTER TRACE TO [USER]

注意:
此命令将使该用户跟踪所有SQL,包括未授权的数据库。

  • 一般的ID、Name:

    new TableDaoRelationship(nameof(PayOrder.User), UserDAO.Instance.TableName)
  • 一般的ID、Name、Value:

    new TableDaoRelationship(nameof(PayOrder.Merchant), MerchantDAO.Instance.TableName).SetOtherFiledsAsIdNameValue(nameof(Merchant.Name), nameof(Merchant.MchId))
  • 一般的ID、Value:

    new TableDaoRelationship(nameof(PayOrder.ProfitSharingPlanOrder), ProfitSharingPlanOrderDAO.Instance.TableName).SetOtherFiledsAsIdValue(nameof(ProfitSharingPlanOrder.Status))
  • 主表ID和关联表ID
    重点在于:SetSelfFieldAlias

    new TableDaoRelationship(nameof(PayOrder.ID), PayOrderTransactionDAO.Instance.TableName).SetSelfFieldAlias(nameof(PayOrder.Transaction)).SetOtherFileds(typeof(PayOrderTransactionInfo))
  • 指定关联表别名
    一般用于查询时需要使用关联表栏位作为查询条件
    重点在于:SetOtherTableAlias,使用时在生成查询条件时要指定Field。

    new TableDaoRelationship(nameof(PayOrder.ID), PayOrderTransactionDAO.Instance.TableName).SetSelfFieldAlias(nameof(PayOrder.Transaction)).SetOtherTableAlias(nameof(SQMIS.Model.PayOrderTransaction)).SetOtherFileds(typeof(PayOrderTransactionInfo))
    .AppendEqual(new Field(nameof(SQMIS.Model.PayOrderTransaction), nameof(SQMIS.Model.PayOrderTransaction.TransactionId)), this.TransactionID)
  • 多重关联时使用SetMoreLogicExpressions,可使用self和other来代替两个表的别名:

    new TableDaoRelationship(nameof(User.Principal), "RBAC_UserPrincipal").SetOtherFiledsAsIdNameValue("Name", "OuterID").SetMoreLogicExpressions(LogicExpression.BuildEqual(new Field("self", "Type"), new Field("other", "Type")))