插入实体


  Fireasy 是类似于 linq to sql 的风格,不像 Entity Framework 在集合里添加实体,最后调用 SaveChanges 方法来持久化。Fireasy 提供了一组 Insert / InsertAsync 方法来插入数据,并且是实时生效的。


1、插入单一实体

  Insert / InsertAsync 接收一个实体对象,将其插入到库中。

[TestMethod]
public void TestInsert()
{
    using (var db = new DbContext())
    {
        var customer = new Customers
            {
                CustomerID = "F1",
                CompanyName = "fireasy",
                City = "kunming"
            };

        db.Customers.Insert(customer);
    }
}

[TestMethod]
public async Task TestInsertAsync()
{
    using (var db = new DbContext())
    {
        var customer = new Customers
            {
                CustomerID = "F1",
                CompanyName = "fireasy",
                City = "kunming"
            };

        await db.Customers.InsertAsync(customer);
    }
}

  该方法返回一个 int 类型的值,如果主键的 GenerateType 为AutoIncrement,那么该方法将返回自动生成的编号(注意:仅主键类型为 int 时返回),否则为影响的记录条数。如果要取主键值,可以取实体的主键值。如下所示:

[TestMethod]
public void TestInsertAndRetPk()
{
    using (var db = new DbContext())
    {
        var order = new Orders
        {
            CustomerID = "ALFKI",
        };

        var ret = db.Orders.Insert(order);

        //测试返回值是否与主键值一致
        Assert.AreEqual(ret, order.OrderID);
    }
}

  必须注意的是,如果使用 new 构造实体,它将不受状态管控,所有非 null 的属性将插入到库中,这样造成的影响是,诸如 int、long、DateTime 类型的属性,会将其默认值直接插入到库中。

  应对的方法可以采用实体类的静态函数 New 和 Wrap。

  New 函数是构造一个经过代理加工后(AOP)的实体对象,因此在对属性赋值的时候,它是受状态管控的。

[TestMethod]
public void TestInsertByNew()
{
    using (var db = new DbContext())
    {
        var customer = Customers.New();
        customer.CustomerID = "F2";
        customer.CompanyName = "fireasy";
        customer.City = "kunming";

        db.Customers.Insert(customer);
    }
}

  Wrap 是封装一个 lambda 表达式,表达式必须成员绑定,它通过分析成员绑定表达式(MemberInitExpression),找出需要插入的属性。

[TestMethod]
public void TestInsertByWrap()
{
    using (var db =  new DbContext())
    {
        var customer = Customers.Wrap(() => new Customers
        {
            CustomerID = "F3",
            CompanyName = "fireasy",
            City = "kunming"
        });

        db.Customers.Insert(customer);
    }
}

2、使用 lambda 表达式插入

  鉴于上例中存在的对象状态的问题,Insert / InsertAsync 还提供另外一组方法,它接收一个 lambda 表达式,其原来是解析成员绑定表达式(MemberInitExpression)中对成员的赋值,构建出一个具有状态的实体对象。因此,它对于字段的写入是按需的、可控的。

[TestMethod]
public void TestInsertByExpression()
{
    using (var db = new DbContext())
    {
        db.Customers.Insert(() => new Customers
        {
            CustomerID = "F3",
            CompanyName = "fireasy",
            City = "kunming"
        });
    }
}

  使用这组方法时应注意,lambda 表达式中的赋值不能出现太过复杂的非常量表达式,比如方法等等。以下的示例中,将不能正确地解析出属性的值。

[TestMethod]
public void TestInsertByExpression()
{
    using (var db = new DbContext())
    {
        var customerId = "F3";

        db.Customers.Insert(() => new Customers
        {
            CustomerID = customerId, //支持变量或参数
            CompanyName = GetCompanyName(), //无法解析方法表达式
            City = "yunnan" + "kunming", //无法解析方法表达式
            EmployeeAmount = 34 * 100 //无法解析运算表达式
        });
    }
}

private string GetCompanyName()
{
    return "fireasy";
}

  另外,该方法不支持带参数的构造表达式(NewExpression),也不能接收一个实体对象(ConstantExpression)。如:

[TestMethod]
public void TestInsertByExpression()
{
    using (var db = new DbContext())
    {
        //以下方法是不支持的
        db.Customers.Insert(() => new Customers("F3"));
                
        //以下方法是不支持的
        var customer = new Customer { CustomerId = "F3" };
        db.Customers.Insert(() => customer);
    }
}


3、使用初始化委托插入

  这一组方法是使用一个委托,对实体对象的属性进行赋值,它同时弥补了前两种方法上的缺陷,从使用的方便性和代码运行性能上来说都是最优的。

[TestMethod]
public void TestInsertByInitializer()
{
    using (var db = new DbContext())
    {
        db.Customers.Insert(c =>
        {
            c.CustomerID = "F3";
            c.CompanyName = "fireasy";
            c.City = "kunming";
        });
    }
}

  委托参数 c 实际上已经是一个 AOP 代理对象了,赋值后能够追踪其属性的状态。


4、使用属性过滤插入

  见 属性过滤