避免通过具有多个worker的Entity Framework在sql表中插入重复项

时间:2017-01-10 09:09:51

标签: c# sql entity-framework

我有一个名为Domain的SQL表,其中包含以下列:

SQL Table

我有多个代理(工作人员)同时在数据库中插入行,我想避免插入重复的域。 Id是主键,我不愿意改变它。

同时我首先检查域是否存在:

public async Task<List<DomainApiModel>> GetListOfExistingDomainsAsync(List<string> domains)
{
    using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
    {
        var rec = await EMME_Context.Domains.Where(p => domains.Contains(p.DomainName)).ToListAsync();
        return rec.Select(p => new DomainApiModel(p)).ToList();
    }
}

然后我过滤那些已经存在的,最后,我通过以下代码插入non exists:

public int Create(List<DomainApiModel> domainApiList, out List<DomainApiModel> domainApiListWithId, int chunkSizeLimit = 500)
{
    using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
    {
        EMME_Context.Configuration.AutoDetectChangesEnabled = false;
        EMME_Context.Configuration.ValidateOnSaveEnabled = false;

        int totalChanges = 0;

        var listOfLists = domainApiList.ChunkSplit(chunkSizeLimit).ToList();
        var listOfDomainData = new List<Domain>();
        foreach (var list in listOfLists)
        {
            foreach (var apiModel in list)
            {
                var objectData = apiModel.GetDataObject();
                EMME_Context.Domains.Add(objectData);
                listOfDomainData.Add(objectData);
            }

            totalChanges += EMME_Context.SaveChanges();
        }

        domainApiListWithId = listOfDomainData.Select(d => new DomainApiModel(d)).ToList();

        return totalChanges;
    }
}

问题在于,在检查域是否存在与创建之间,另一个代理可以插入相同的域,并且我在表中有重复项。

任何人都知道如何解决这个问题?

Obs:我对一个名为page的表有同样的问题,其中包含&#34; url列&#34;这是nvarchar950类型,因此只创建一个唯一索引不是解决方案...

2 个答案:

答案 0 :(得分:2)

可以轻松解决此问题,为表添加唯一索引。任何添加重复值的尝试都会引发异常。

CREATE UNIQUE INDEX UX_DOMAIN_NAME
ON DOMAIN (DOMAIN_NAME)

请注意,它将要求您独立添加每个新值,否则整个事务将失败,即使对于那些非重复的值也是如此。

foreach (var list in listOfLists)
        {
            foreach (var apiModel in list)
            {
                var objectData = apiModel.GetDataObject();
                EMME_Context.Domains.Add(objectData);
                listOfDomainData.Add(objectData);
                try{
                  totalChanges += EMME_Context.SaveChanges();
                }
                 catch(SqlException se){
                    if(se.Number != 2601) // Unique key violation
                    {
                       // Handle other errors
                    }
                 }
            }
}

答案 1 :(得分:1)

在所有需要唯一的列上添加其他索引。要编制索引的大型列可以通过计算散列索引。

这里有一个创建许多实体的概念以及如何跟踪异常:

class MyService
{

    public async Task<OperationResult<string, SomeEntity>> CreateManyAsync( IList<string> data, int chunkSize )
    {
        var succeded = new List<SomeEntity>( );
        var failed = new List<FailedOperation<string>>( );

        foreach ( var chunk in data.Select( ( dataItem, index ) => new { data = dataItem, chunk = index % chunkSize } ).GroupBy( c => c.chunk, c => c.data ) )
        {
            try
            {
                succeded.AddRange( await InternalCreateManyAsync( chunk ) );
                continue;
            }
            catch ( Exception )
            {
                // we just eat this exception
            }

            foreach ( var singleItem in chunk )
            {
                try
                {
                    succeded.Add( await InternalCreateSingleAsync( singleItem ) );
                }
                catch ( Exception ex )
                {
                    failed.Add( new FailedOperation<string>( singleItem, ex ) );
                }
            }
        }

        return new OperationResult<string, SomeEntity> {
            Succeded = succeded,
            Failed = failed,
        };
    }

    private async Task<IList<SomeEntity>> InternalCreateManyAsync( IEnumerable<string> data )
    {
        var result = new List<SomeEntity>( );

        using ( var db = new MyCOntext( ) )
        {
            foreach ( var item in data )
            {
                result.Add( AddSingleToContext( item, db ) );
            }
            await db.SaveChangesAsync( );
        }

        return result;
    }

    private async Task<SomeEntity> InternalCreateSingleAsync( string data )
    {
        using ( var db = new MyContext( ) )
        {
            var e = AddSingleToContext( data, db );
            await db.SaveChangesAsync( );
            return e;
        }
    }

    private SomeEntity AddSingleToContext( string data, MyContext context )
    {
        var entity = new SomeEntity { Data = data, };
        context.SomeEntities.Add( entity );
        return entity;
    }

}

一些实用程序类

class SomeEntity
{
    public int Id { get; set; }
    public string Data { get; set; }
}

class FailedOperation<T>
{
    public FailedOperation( T data, Exception error )
    {
        Data = data;
        Error = error;
    }

    public T Data { get; }
    public Exception Error { get; }
}

class OperationResult<TSource, TResult>
{
    public IList<TResult> Succeded { get; set; }
    public IList<FailedOperation<TSource>> Failed { get; set; }
}