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());