首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在实体框架6中使用T序列

问在实体框架6中使用T序列
EN

Code Review用户
提问于 2016-12-16 11:04:32
回答 1查看 8.8K关注 0票数 5

在阅读了许多关于如何处理EF 6中的序列的文章之后(见下文),我找到了一种适合我的方法。我首先使用代码,希望从头到尾包含序列,所以不使用迁移(告诉我是否弄错了)。

任务是为我的实体车获得一个唯一的和不累加的额外ID,这个ID没有被用作主键。

我现在正在做的事情是:

实体:

代码语言:javascript
复制
public class Car
{
    public int Id { get; set; }
    public int CarNr { get; set; }
    public string Name { get; set; }
}

上下文:

代码语言:javascript
复制
public class Context : DbContext
{
    public Context() : base()
    {
        SetDefaultConfiguration();
    }

    public Context(string connectionString) : base(connectionString)
    {
        SetDefaultConfiguration();
    }

    public Context(DbConnection existingConnection) : base(existingConnection, true)
    {
        SetDefaultConfiguration();
    }

    public override int SaveChanges()
    {
        SetCarNr();
        return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync()
    {
        SetCarNr();
        return base.SaveChangesAsync();
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        SetCarNr();
        return base.SaveChangesAsync(cancellationToken);
    }

    private void SetDefaultConfiguration()
    {
        Database.SetInitializer(new CreateSequenceInitializer());
    }

    public class CreateSequenceInitializer : DropCreateDatabaseIfModelChanges<Context>
    {
        protected override void Seed(Context context)
        {
            context.Database.ExecuteSqlCommand("CREATE SEQUENCE CarNrSequence AS INT START WITH 200000 NO CACHE;");
            base.Seed(context);
        }
    }

    private void SetCarNr()
    {
        var carsToSave = ChangeTracker.Entries().Select(e => e.Entity).OfType<Car>();
        foreach (var c in carsToSave)
        {
            c.CarNr = GetNextSequenceValue();
        }
    }

    public int GetNextSequenceValue()
    {
        var rawQuery = Database.SqlQuery<int>("SELECT NEXT VALUE FOR CarNrSequence;");
        var task = rawQuery.SingleAsync();
        int nextVal = task.Result;
        return nextVal;
    }

    public DbSet<Car> Cars { get; set; }
}

因此,我使用一个DatabaseInitializer为我创建序列,并拦截SaveChanges以插入从DB查询的额外ID。

问:有没有更好的方法?这是“拯救”吗?你看到什么问题了?

谢谢!

PS:这里有一些链接对我很有帮助

EN

回答 1

Code Review用户

发布于 2017-04-05 13:56:01

您的C#代码看起来很好,但是使用非缓存的序列而不是标识(在实体框架中实现起来要容易得多)并不能确保无障碍地递增标识符。

根据微软的这篇文章,序列可以在一些情景中产生空白:

序列对象根据其定义生成数字,但序列对象不控制数字的使用方式。当事务回滚时,当多个表共享序列对象时,或者在没有在表中使用序列号时,插入到表中的序列号可能会有空白。

因此,保存更改的错误将消耗序列号,从而获得空白。

在数据库方面,这条线接近于生成无障碍增量标识符,但我也在考虑在应用层中生成标识符和恢复保存失败时的值:

代码语言:javascript
复制
private int PrevSequenceId;
private int SequenceId;

private void SetCarNr()
{
    var carsToSave = ChangeTracker.Entries().Select(e => e.Entity).OfType<Car>();
    foreach (var c in carsToSave)
    {
        c.CarNr = Interlocked.Increment(ref SequenceId);
    }
}

public override int SaveChanges()
{
    try
    {
        SetCarNr();
        return base.SaveChanges();
        // locking might be required here for thread safety
        PrevSequenceId = SequenceId;
    }
    catch (Exception exc)
    {
        // log stuff if needed

        // locking might be required here for thread safety
        SequenceId = PrevSequenceId;
    }
}

但是,只有在仅由应用层生成标识符时,此解决方案才能工作。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/150060

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档