increment field in entity framework depending on v

2019-05-28 19:19发布

I'm new in Entity Framework and i have problem with something which was quite easy in pure t-sql. Assume that we have table with 3 columns. Id(identity, primary key), IdKind (int), Number(int). Column Nubmer depends on IdKind column. Data in this table should look like this:

Id | IdKind | Number

1 | 1 | 1

2 | 1 | 2

3 | 1 | 3

4 | 2 | 1

5 | 2 | 2

6 | 2 | 3

As you can see column number is auto incremented depends on IdKind. In t-sql I was finding max(Number) + 1 for some IdKind. Everything was in one stored procedure in one transaction so value of Number was unique for IdKind.

How to accomplish the same in Entity Framework. Value of Number should be computed when creating new object or when changing IdKind.

2条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-05-28 20:02

I had the same problem but I solved it in c#.

I should have an entity mapped to the table:

public class MyEntity
{ 
     Kind IdKind{get; set;}
     int Number{get;set;}
}

I should have and auxiliary entity to store the last number used per Kind.

public class LastNumber
{ 
     Kind IdKind{get;set;}
     int LastNumber{get;set;}
}

Then before adding a row (MyEntity object) to the table, I should calculate the proper Number using the LastNumber field of the LastNumberEntity with Kind equals to my Kind value.

We must note that this strategy has a concurrency problem because two different threads could be adding an object of the same Kind at the same time, those threads will inspect the same LastNumberEntity to get the LastNumber value. This problem has a known solution in entityframework. The code could looks like this:

 public int GetNumber(Kind idKind)
    {
        using (var db = new MyDataContext())
        {
            var lastNumber = db.LastNumbers.Single(x=>x.IdKind == idKind);
            for (int attemps = 0; attemps < 10;)
            {
                try
                {
                    lastNumber.LastNumber++;
                    db.SaveChanges();
                    return lastNumber.LastNumber;
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    attemps++;
                }
            }
        }

        throw new Exception("Many concurrency calls");
    }

This method gets an incremental number for a specified Kind and handles the concurrency problem, using it, we can set the Number value for each MyEntity object. The client code could looks like this:

var entity = new MyEntity();
var number = GetNumber(entity.IdKind);
entity.Number = number;
db.MyEntities.Add(entity);
db.SaveChanges();

This code could be refactored, making the setters private, making a factory to ensure the entities always are created using the GetNumber method and using repositories to manage the db.

查看更多
家丑人穷心不美
3楼-- · 2019-05-28 20:03

I would use database trigger for that and configure Number property in EF mapping with StoreGeneratedPattern.Computed to inform EF that this property is filled in the database and must be reloaded after each update / insert.

I would also not use max() for each new number. Instead I would maintain separate table simulating sequences. This table should contain record per sequence - in your case record per IdKind with current max number. Instead of selecting aggregation you will select single record and update it to contain new max number. Working with this table must be in the same transaction as persisting changes to your entity.

查看更多
登录 后发表回答