Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架GetOrCreate?_C#_Entity Framework_Entity Framework 6_Azure Sql Database - Fatal编程技术网

C# 实体框架GetOrCreate?

C# 实体框架GetOrCreate?,c#,entity-framework,entity-framework-6,azure-sql-database,C#,Entity Framework,Entity Framework 6,Azure Sql Database,首先是我的桌子。不是很有趣,只是一个主键,数据和外键连接它们。一切都有一个独特的约束 CREATE TABLE [dbo].[Providers] ( [id] BIGINT IDENTITY (1, 1) NOT NULL, [name] NVARCHAR (850) NOT NULL, CONSTRAINT [PK_Providers] PRIMARY KEY CLUSTERED ([id] ASC), CONSTRAINT [UQ_Pro

首先是我的桌子。不是很有趣,只是一个主键,数据和外键连接它们。一切都有一个独特的约束

CREATE TABLE [dbo].[Providers] (
    [id]   BIGINT         IDENTITY (1, 1) NOT NULL,
    [name] NVARCHAR (850) NOT NULL,
    CONSTRAINT [PK_Providers] PRIMARY KEY CLUSTERED ([id] ASC),
    CONSTRAINT [UQ_Providers_name] UNIQUE NONCLUSTERED ([name] ASC)
);

使用实体框架,我想在
模板中插入一行。但是,在插入时,我不知道
Providers
中的匹配行是否存在。如果有很多人同时插入(两个模板具有相同的
提供程序
),我想对比赛条件进行防御

我相信,为了实现这一点,我需要首先获取或创建提供者,然后再持久化(至少一次完整的DB往返)创建模板

我已经想出了一个解决方案,我认为会奏效,但它似乎难以置信地复杂,因为我必须想象的是一个非常常见的场景。我希望有人能告诉我我做错了什么,或者我怎样才能做得更好

下面的代码存在于我的
DbContext
实现中

public async Task<Provider> GetOrCreateProvider(String name)
{
    var provider = new Provider { name = name };
    return await GetOrCreateEntity(Providers, provider);
}

public async Task<Template> GetOrCreateTemplate(Provider provider, String repositoryId)
{
    var template = new Template { Provider = provider, repositoryId = repositoryId };
    return await GetOrCreateEntity(Templates, template);
}

private async Task<T> GetOrCreateEntity<T>(DbSet<T> dbSet, T newEntity) where T : class
{
    var maybeEntity = await dbSet.Where(entity => entity.Equals(newEntity)).FirstOrDefaultAsync();
    if (maybeEntity != null)
        return maybeEntity;

    try
    {
        dbSet.Add(newEntity);
        await SaveChangesAsync();
        return newEntity;
    }
    catch (UpdateException exception) when ((exception.InnerException as SqlException)?.Number == 2601 || (exception.InnerException as SqlException)?.Number == 2627)
    {
        return await dbSet.Where(entity => entity.Equals(newEntity)).FirstOrDefaultAsync();
    }
}
用法如下所示:

var provider = await GetOrCreateProvider("foo");
var template = await GetOrCreateTemplate(provider, "bar");
这方面的主要问题(除了执行如此简单的操作所需的大量样板文件之外)是,在最佳情况下(当提供者和模板都已存在时),我必须对服务器进行两次完整的往返。在最坏的情况下,我必须往返6次:

  • 无法获取提供程序
  • 约束冲突创建提供程序
  • 获取提供者
  • 无法获取模板
  • 约束冲突创建模板
  • 获取模板

  • 我并不十分关心最坏的情况,但我关心的是最好的情况,因为往返次数随着对象图的复杂性而增加。如果我有其他引用模板表中某个内容的内容,我现在最多有3次往返。

    存储过程将在对数据库的一次调用和一次事务中完成所有步骤。例如,请查看
    merge
    命令或

    您可以使用此方法从EF执行存储过程:

    private static int ExecuteSqlCount(string statement, SqlParameter[] paramsSql)
    {
        using (Entities dbContext = new Entities())
        {
            var total = dbContext.Database.SqlQuery<int>(statement, paramsSql).First();
            return total;
        }
    }
    
    private static int ExecuteSqlCount(字符串语句,SqlParameter[]paramsql)
    {
    使用(Entities dbContext=new Entities())
    {
    var total=dbContext.Database.SqlQuery(语句,paramsSql.First();
    返回总数;
    }
    }
    
    返回值是USP返回的值。该方法的调用方式如下(对于返回int的USP):

    var参数=新列表();
    string语句=“exec uspersonadd@personName=@name,@activeFlag=@active”;
    parameters.Add(新的SqlParameter(“@name”,person.PersonName));
    添加(新的SqlParameter(“@active”,person.active));
    int id=ExecuteSqlCount(语句,parameters.ToArray());
    

    您需要
    使用System.Data.SqlClient

    存储过程将在对数据库的单个调用和单个事务中完成所有步骤。例如,请查看
    merge
    命令或

    您可以使用此方法从EF执行存储过程:

    private static int ExecuteSqlCount(string statement, SqlParameter[] paramsSql)
    {
        using (Entities dbContext = new Entities())
        {
            var total = dbContext.Database.SqlQuery<int>(statement, paramsSql).First();
            return total;
        }
    }
    
    private static int ExecuteSqlCount(字符串语句,SqlParameter[]paramsql)
    {
    使用(Entities dbContext=new Entities())
    {
    var total=dbContext.Database.SqlQuery(语句,paramsSql.First();
    返回总数;
    }
    }
    
    返回值是USP返回的值。该方法的调用方式如下(对于返回int的USP):

    var参数=新列表();
    string语句=“exec uspersonadd@personName=@name,@activeFlag=@active”;
    parameters.Add(新的SqlParameter(“@name”,person.PersonName));
    添加(新的SqlParameter(“@active”,person.active));
    int id=ExecuteSqlCount(语句,parameters.ToArray());
    

    您需要
    使用System.Data.SqlClient

    如何创建实体类?你能给他们看一下吗?
    newprovider(“foo”)
    我还简化了代码,这样你现在可以更清楚地看到
    newprovider(“foo”)
    了。我现在意识到你指的是类本身。我用我的一个班级编辑过。它是Visual Studio使用新的ADO.NET实体框架向导从数据库中自动创建的。在开始使用C#编写代码之前,我将始终从SQL的角度考虑这一点。如果我纯粹在SQL中执行此操作,是否需要担心上述所有步骤?。如果是这样的话,当我在EF中执行相同操作时,我将不得不考虑所有这些步骤。如何创建实体类?你能给他们看一下吗?
    newprovider(“foo”)
    我还简化了代码,这样你现在可以更清楚地看到
    newprovider(“foo”)
    了。我现在意识到你指的是类本身。我用我的一个班级编辑过。它是Visual Studio使用新的ADO.NET实体框架向导从数据库中自动创建的。在开始使用C#编写代码之前,我将始终从SQL的角度考虑这一点。如果我纯粹在SQL中执行此操作,是否需要担心上述所有步骤?。如果是这样,当我在EF中做同样的事情时,我将不得不考虑所有这些步骤。我正在寻找一种从实体框架实现这一点的方法。您的建议是否可能来自实体框架?是的,我使用此方法执行存储过程,如我编辑的答案所示。如果我正确理解您在这里所做的,那么这并不是真正利用实体框架。它只是对数据库进行常规SQL调用。如果我要走这条路,我最好放弃实体框架,使用传统的SQL。特别是,他说:“从数据库上的SqlQuery返回的结果永远不会被上下文跟踪,即使对象是实体类型的实例。”所以这样做可能会导致长期维护问题。我正在寻找一种从实体框架中实现这一点的方法。您所建议的实体框架是否可行?是的,我使用此方法来执行存储过程,如我编辑的答案所示。如果我正确理解您在这里所做的,那么这并不是真正利用E
    private static int ExecuteSqlCount(string statement, SqlParameter[] paramsSql)
    {
        using (Entities dbContext = new Entities())
        {
            var total = dbContext.Database.SqlQuery<int>(statement, paramsSql).First();
            return total;
        }
    }
    
        var parameters = new List<SqlParameter>();
        string statement = "exec uspPersonAdd @personName = @name, @activeFlag = @active";
        parameters.Add(new SqlParameter("@name", person.PersonName));
        parameters.Add(new SqlParameter("@active", person.Active));
        int id = ExecuteSqlCount(statement, parameters.ToArray());