Como modificar a chave estrangeira de um registro utilizando Linq to Entities?
Amigos, boa tarde.
O cenário é o seguinte:
Tenho um registro de Produto que contém o seu código identity como chave primária.
Esse registro contém várias chaves estrangeiras, entre elas CódigoGrupo, CódigoGrupoSub, CódigoFamília.
Eu gostaria de fazer uma atualização em alguns registros, modificando as chaves estrangeiras apenas.
Ex:
Id Descrição Grupo SubGrupo Familia
1 Cimento 1 1 1
2 Cal 3 2 1
3 Tijolo 4 3 4
4 Bloco 1 1 9
Eu gostaria de reorganizar esses produtos, modificando seus Grupos, SubGrupos, Famílias.
Quando eu edito diretamente na base de dados (SQL Server 2008), o banco me deixa atualizar perfeitamente, apenas restringe se realmente existe os novos registros de Grupo, Subgrupo e Familia nas tabelas correspondentes.
Portanto eu posso passar de Grupo 6, SubGrupo 3, Familia 9 para Grupo 10 Subgrupo 10 Familia 10, desde que os segundos registros existam em suas tabelas respectivas.
Mas quando eu tento update via aplicação, utilizando Linq to Entities, eu obtenho a seguinte mensagem:
A propriedade 'codigoGrupoSubFamilia' faz parte das informações de chave do objeto e não pode ser modificada.
A mensagem vem em português mesmo, estou utilizando a versão traduzida do VS2008 e do SQL Server 2008 (que eu pessoalmente não gosto, mas a empresa optou pela versão traduzida).
Utilizando linguagem c#.
No aguardo, e se necessário posso postar scripts, métodos, código, qualquer coisa que seja necessária.
O cenário é o seguinte:
Tenho um registro de Produto que contém o seu código identity como chave primária.
Esse registro contém várias chaves estrangeiras, entre elas CódigoGrupo, CódigoGrupoSub, CódigoFamília.
Eu gostaria de fazer uma atualização em alguns registros, modificando as chaves estrangeiras apenas.
Ex:
Id Descrição Grupo SubGrupo Familia
1 Cimento 1 1 1
2 Cal 3 2 1
3 Tijolo 4 3 4
4 Bloco 1 1 9
Eu gostaria de reorganizar esses produtos, modificando seus Grupos, SubGrupos, Famílias.
Quando eu edito diretamente na base de dados (SQL Server 2008), o banco me deixa atualizar perfeitamente, apenas restringe se realmente existe os novos registros de Grupo, Subgrupo e Familia nas tabelas correspondentes.
Portanto eu posso passar de Grupo 6, SubGrupo 3, Familia 9 para Grupo 10 Subgrupo 10 Familia 10, desde que os segundos registros existam em suas tabelas respectivas.
Mas quando eu tento update via aplicação, utilizando Linq to Entities, eu obtenho a seguinte mensagem:
A propriedade 'codigoGrupoSubFamilia' faz parte das informações de chave do objeto e não pode ser modificada.
A mensagem vem em português mesmo, estou utilizando a versão traduzida do VS2008 e do SQL Server 2008 (que eu pessoalmente não gosto, mas a empresa optou pela versão traduzida).
Utilizando linguagem c#.
No aguardo, e se necessário posso postar scripts, métodos, código, qualquer coisa que seja necessária.
Daniel Vieira
Curtidas 0
Respostas
Luiz Maia
03/11/2009
Ola Andre, tudo bom?
Seguinte, temos tido muitas duvidas quanto ao L2E, e eu sempre que posso, recomendo NÃO usar, tem muita coisa errada ainda e de dificil implementação, mas como sempre, o pessoal fala que ja esta no meio do projeto e etc...
Mas é o seguinte, L2S é muito mais simples a estável, mas ja que esta usando L2E, faça o seguinte:
linq-to-entities record r = new record();
r.cityReference.EntityKey = new EntityKey("YourEntitiesNamespace.cities", "city_id", 5); Exemplo acima: Note que o "record" tem a propriedade cityReference, que no seu caso sera familiaReference e assim por diante... Esta propriedade é automaticamente gerada edmx justamente por causa da referencia em outra entidade(table). Este new EntityKey tem apenas 3 parametros: 1 - qualifiedEntitySetName(string): que sera o entityNameSpace.TableName. 2 - KeyName(string): campo da PK 3 - KeyValue(object): valor do campo PK Espero ter ajudado. Aguardo seu retorno, ok? Referencia: http://stackoverflow.com/questions/826537/how-do-i-update-an-objects-foreign-key-value-with-linq-to-entities Abraços Att Luiz Maia
r.cityReference.EntityKey = new EntityKey("YourEntitiesNamespace.cities", "city_id", 5); Exemplo acima: Note que o "record" tem a propriedade cityReference, que no seu caso sera familiaReference e assim por diante... Esta propriedade é automaticamente gerada edmx justamente por causa da referencia em outra entidade(table). Este new EntityKey tem apenas 3 parametros: 1 - qualifiedEntitySetName(string): que sera o entityNameSpace.TableName. 2 - KeyName(string): campo da PK 3 - KeyValue(object): valor do campo PK Espero ter ajudado. Aguardo seu retorno, ok? Referencia: http://stackoverflow.com/questions/826537/how-do-i-update-an-objects-foreign-key-value-with-linq-to-entities Abraços Att Luiz Maia
GOSTEI 0
Daniel Vieira
03/11/2009
record r = new record();
r.cityReference.EntityKey = new EntityKey("YourEntitiesNamespace.cities", "city_id", 5); Exemplo acima: Note que o "record" tem a propriedade cityReference, que no seu caso sera familiaReference e assim por diante... Esta propriedade é automaticamente gerada edmx justamente por causa da referencia em outra entidade(table). Este new EntityKey tem apenas 3 parametros: 1 - qualifiedEntitySetName(string): que sera o entityNameSpace.TableName. 2 - KeyName(string): campo da PK 3 - KeyValue(object): valor do campo PK
Eu utilizo o L2E exatamente pelo que você disse, estamos no meio do projeto, e o início do projeto foi adquirido de terceiros, nem tivemos opção.
A propriedade Reference eu utilizo Reference.Value pra atribuir a referencia da FK em um novo registro a ser gravado.
newProduto.GrupoSubFamiliaReference.Value = newGrupoSubFamilia;
Agora EntityKey eu nunca utilizei, e acabei não entendendo os parâmetros muito bem.
Poderia me dar um exemplo, utilizando a minha nomenclatura?
Tabela pai (que eu quero atualizar as fks)
Produto
Tabelas estrangeiras
Grupo
GrupoSub
GrupoSubFamilia
Digamos que eu quero atualizar o Campo codigoGrupoSubFamilia do registro de Produtos, de 3 para 1, qual seriam os parâmtros que eu teria que passar?
r.cityReference.EntityKey = new EntityKey("YourEntitiesNamespace.cities", "city_id", 5); Exemplo acima: Note que o "record" tem a propriedade cityReference, que no seu caso sera familiaReference e assim por diante... Esta propriedade é automaticamente gerada edmx justamente por causa da referencia em outra entidade(table). Este new EntityKey tem apenas 3 parametros: 1 - qualifiedEntitySetName(string): que sera o entityNameSpace.TableName. 2 - KeyName(string): campo da PK 3 - KeyValue(object): valor do campo PK
Eu utilizo o L2E exatamente pelo que você disse, estamos no meio do projeto, e o início do projeto foi adquirido de terceiros, nem tivemos opção.
A propriedade Reference eu utilizo Reference.Value pra atribuir a referencia da FK em um novo registro a ser gravado.
newProduto.GrupoSubFamiliaReference.Value = newGrupoSubFamilia;
Agora EntityKey eu nunca utilizei, e acabei não entendendo os parâmetros muito bem.
Poderia me dar um exemplo, utilizando a minha nomenclatura?
Tabela pai (que eu quero atualizar as fks)
Produto
Tabelas estrangeiras
Grupo
GrupoSub
GrupoSubFamilia
Digamos que eu quero atualizar o Campo codigoGrupoSubFamilia do registro de Produtos, de 3 para 1, qual seriam os parâmtros que eu teria que passar?
GOSTEI 0
Daniel Vieira
03/11/2009
Eu utilizo o L2E exatamente pelo que você disse, estamos no meio do projeto, e o início do projeto foi adquirido de terceiros, nem tivemos opção.
A propriedade Reference eu utilizo Reference.Value pra atribuir a referencia da FK em um novo registro a ser gravado.
newProduto.GrupoSubFamiliaReference.Value = newGrupoSubFamilia;
Agora EntityKey eu nunca utilizei, e acabei não entendendo os parâmetros muito bem.
Poderia me dar um exemplo, utilizando a minha nomenclatura?
Tabela pai (que eu quero atualizar as fks)
Produto
Tabelas estrangeiras
Grupo
GrupoSub
GrupoSubFamilia
Digamos que eu quero atualizar o Campo codigoGrupoSubFamilia do registro de Produtos, de 3 para 1, qual seriam os parâmtros que eu teria que passar?
GOSTEI 0
Luiz Maia
03/11/2009
Opa, seria algo assim:
int codigoGrupoSubFamilia = 1; //aqui seu novo valor a ser atualizado ok?
Produto.ProdutoIdreference.EntityKey = new EntityKey("SuaEntities.GrupoSubFamilia", "CodGrupoSubFamilia", codigoGrupoSubFamilia);
Lembrando: CodGrupoSubFamilia = PK da entidade Familia;
Aguardo...
Abraços
Att
Luiz Maia
GOSTEI 0
Daniel Vieira
03/11/2009
A pk da entidade família é composta, na verdade é uma cascata...
Pra esclarecer melhor vou postar a organização das tabelas:
tblGrupo
codigoGrupo
Descricao
tblGrupoSub
codigoGrupo FK
codigoGrupoSub
Descricao
tblFamilia
codigoGrupo FK
codigoGrupoSub FK
codigoGrupoSubFamilia
Descricao
tblProduto
codigoProduto
Descricao
codigoGrupo FK
codigoGrupoSub FK
codigoGrupoSubFamilia FK
Campos sublinhados são PK
Eu preciso trocar os valores das 3 FKs na tabela de produto.
Pra navegar entre os 3 valores eu tenho acesso apenas navegando Produto.GrupoSubFamilia
Pra trocar esses 3 valores como ficaria o exemplo?
Pra esclarecer melhor vou postar a organização das tabelas:
tblGrupo
codigoGrupo
Descricao
tblGrupoSub
codigoGrupo FK
codigoGrupoSub
Descricao
tblFamilia
codigoGrupo FK
codigoGrupoSub FK
codigoGrupoSubFamilia
Descricao
tblProduto
codigoProduto
Descricao
codigoGrupo FK
codigoGrupoSub FK
codigoGrupoSubFamilia FK
Campos sublinhados são PK
Eu preciso trocar os valores das 3 FKs na tabela de produto.
Pra navegar entre os 3 valores eu tenho acesso apenas navegando Produto.GrupoSubFamilia
Pra trocar esses 3 valores como ficaria o exemplo?
GOSTEI 0
Luiz Maia
03/11/2009
Andre, pelo que entendi, não é Chave Composta que vc tem ai.
Sua modelagem me parece equivocada, pois não tem necessidade de vc ter codGrupo, e codSubGrupo na tabela Produto, pois so com o codigo da Familia, vc tera os outros. Ja que uma Familia pertence a um SubGrupo e um subGrupo pertence a um Grupo, correto?
Aguardo
GOSTEI 0
Daniel Vieira
03/11/2009
Na verdade não.
Uma família número 1 pode pertencer ao Grupo 1 + SubGrupo 1, e pode também pertencer ao Grupo 1 + Subgrupo 2.
Ex
Grupo GrupoSub Familia
1 1 1
1 2 1
1 2 2
1 2 3
A chave é baseada na composição das chaves anteriores
Uma família número 1 pode pertencer ao Grupo 1 + SubGrupo 1, e pode também pertencer ao Grupo 1 + Subgrupo 2.
Ex
Grupo GrupoSub Familia
1 1 1
1 2 1
1 2 2
1 2 3
A chave é baseada na composição das chaves anteriores
GOSTEI 0
Daniel Vieira
03/11/2009
Olha o MER pra ficar mais claro
GOSTEI 0
Luiz Maia
03/11/2009
Andre, como esta seu mapeamento?
Veja neste exemplo abaixo de Mestre-Detalhe:
GOSTEI 0
Daniel Vieira
03/11/2009
O mapeamento da GrupoSubFamilia parece estar correto.
Mas o da produto esta em branco, da uma olhada:
GrupoSubFamilia
Produto
Mas o da produto esta em branco, da uma olhada:
GrupoSubFamilia
Produto
GOSTEI 0
Luiz Maia
03/11/2009
Realmente esta faltando algo, tente refazer o Mapeamento. Atente para a propriedade de Multiplicidade da figura que te mandei... Aguardo seu retorno, ok?
Abraços
Att
Luiz Maia
GOSTEI 0
Daniel Vieira
03/11/2009
Desculpe Luiz foi erro meu.
Eu cliquei diretamente na tabela, clicando no mapeamento ficaria assim:
Eu cliquei diretamente na tabela, clicando no mapeamento ficaria assim:
GOSTEI 0
Daniel Vieira
03/11/2009
Consegui atualizar a FK UnidadeMedida da tabela de produtos, utilizando o metodo:
transfProduto.UnidadeMedidaReference.EntityKey =new EntityKey(
"Entities.UnidadeMedida",
"codigoUnidadeMedida",
"L");
Quando eu tento atualizar as chaves de GrupoSubFamilia, utilizando
transfProduto.GrupoSubFamiliaReference.EntityKey = new EntityKey(
"ModelDB.GrupoSubFamilia",
"codigoGrupoSubFamilia",
codGrupoSubFamilia);
Eu recebo a seguinte mensagem:
A lista de pares chave-valor fornecida contém um número incorreto de entradas. Há 3 campos de chave definidos no tipo 'CONSTRUSYSModel.GrupoSubFamilia' mas 1 foram fornecidos.
Nom do parâmetro: value
A mensagem vem em português.
Deve ser porque estou passando apenas 1 das chaves, e ela é chave composta.Como eu passaria os outros valores?
transfProduto.UnidadeMedidaReference.EntityKey =new EntityKey(
"Entities.UnidadeMedida",
"codigoUnidadeMedida",
"L");
Quando eu tento atualizar as chaves de GrupoSubFamilia, utilizando
transfProduto.GrupoSubFamiliaReference.EntityKey = new EntityKey(
"ModelDB.GrupoSubFamilia",
"codigoGrupoSubFamilia",
codGrupoSubFamilia);
Eu recebo a seguinte mensagem:
A lista de pares chave-valor fornecida contém um número incorreto de entradas. Há 3 campos de chave definidos no tipo 'CONSTRUSYSModel.GrupoSubFamilia' mas 1 foram fornecidos.
Nom do parâmetro: value
A mensagem vem em português.
Deve ser porque estou passando apenas 1 das chaves, e ela é chave composta.Como eu passaria os outros valores?
GOSTEI 0
Luiz Maia
03/11/2009
Andre, estamos evoluindo!!! rsrs
Olha so, dei um olhada nas referencias da Microsoft para criar chaves compostas e descobri isto, veja se funciona:
using (AdventureWorksEntities advWorksContext =
new AdventureWorksEntities())
{
try
{
Object entity = null;
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 43680) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey("AdventureWorksEntities.SalesOrderHeader", entityKeyValues);
// Get the object from the context or the persisted store by its key.
if (advWorksContext.TryGetObjectByKey(key, out entity))
{
Console.WriteLine("The requested " + entity.GetType().FullName +
" object was found");
}
else
{
Console.WriteLine("An object with this key " +
"could not be found.");
}
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
} Aqui a referencia completa da MS: http://msdn.microsoft.com/en-us/library/dd283138.aspx Vaja que são criados pares de chaves no exemplo acima: IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 43680) };
Isto se deve a sobrecarga deste metodo existente no new EntityKey. Aguardo Att Luiz Maia
new AdventureWorksEntities())
{
try
{
Object entity = null;
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 43680) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey("AdventureWorksEntities.SalesOrderHeader", entityKeyValues);
// Get the object from the context or the persisted store by its key.
if (advWorksContext.TryGetObjectByKey(key, out entity))
{
Console.WriteLine("The requested " + entity.GetType().FullName +
" object was found");
}
else
{
Console.WriteLine("An object with this key " +
"could not be found.");
}
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
} Aqui a referencia completa da MS: http://msdn.microsoft.com/en-us/library/dd283138.aspx Vaja que são criados pares de chaves no exemplo acima: IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 43680) };
Isto se deve a sobrecarga deste metodo existente no new EntityKey. Aguardo Att Luiz Maia
GOSTEI 0
Daniel Vieira
03/11/2009
então se eu q
GOSTEI 0
Daniel Vieira
03/11/2009
então se eu quisesse uma colecao de chaves com 4 chaves teria que fazer isso?
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 1)
new KeyValuePair<string, object>("SalesOrderID", 2)
new KeyValuePair<string, object>("SalesOrderID", 3)
new KeyValuePair<string, object>("SalesOrderID", 4)
};
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("SalesOrderID", 1)
new KeyValuePair<string, object>("SalesOrderID", 2)
new KeyValuePair<string, object>("SalesOrderID", 3)
new KeyValuePair<string, object>("SalesOrderID", 4)
};
GOSTEI 0
Luiz Maia
03/11/2009
Andre,
O que esta na instrução da MS é isto mesmo, mas ainda não estei aqui.
Você compilou a aplicação?
Deu alguma excessão?
Aguardo
Att
Luiz Maia
GOSTEI 0
Daniel Vieira
03/11/2009
Funcionou perfeitamente Luiz, muito grato a ajuda.
vou postar meu método de alteração:
private void TransfereCodigos(int codGrupo, int codGrupoSub, int codGrupoSubFamilia,
List<Int32> listaTransferir)
{
try
{
ModelDB.Entities db = new ModelDB.Entities();
int i = 0;
foreach (int a in listaTransferir)
{
int cod = listaTransferir[i];
var transfProduto = (from p in db.Produto
where (p.codigo == cod)
select p).First();
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[]
{
new KeyValuePair<string, object>("codigoGrupoSubFamilia", codGrupoSubFamilia),
new KeyValuePair<string, object>("codigoGrupoSub", codGrupoSub),
new KeyValuePair<string, object>("codigoGrupo", codGrupo)
};
transfProduto.GrupoSubFamiliaReference.EntityKey = new EntityKey(
"Entities.GrupoSubFamilia",
entityKeyValues);
transfProduto.dataUltimaAtualizacaoCadastral = System.DateTime.Now;
db.SaveChanges();
i++;
MessageBox.Show("Produtos Alterados com sucesso!");
}
}
catch (Exception ex)
{
MessageBox.Show("Problema ao Atualizar\n"
+ ex.Message,
"Atualizar Produto");
}
}
So pra complementar:
O que é exatamente o IEnumerable?
Um tipo de array?
Porque pelo que eu percebi no C# o array é chamado de list, e contém muito mais funcionalidades.
Ainda sou iniciante, 2 meses de programação profissional (exceto um estágio a 2 anos atrás que fiz em Java).
Antes eu atuava como analista de suporte.
No mais obrigado, se puder tirar minha última dúvida, só pra mim saber qual o mecanismo que eu utilizei, eu agradeceria.
vou postar meu método de alteração:
private void TransfereCodigos(int codGrupo, int codGrupoSub, int codGrupoSubFamilia,
List<Int32> listaTransferir)
{
try
{
ModelDB.Entities db = new ModelDB.Entities();
int i = 0;
foreach (int a in listaTransferir)
{
int cod = listaTransferir[i];
var transfProduto = (from p in db.Produto
where (p.codigo == cod)
select p).First();
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[]
{
new KeyValuePair<string, object>("codigoGrupoSubFamilia", codGrupoSubFamilia),
new KeyValuePair<string, object>("codigoGrupoSub", codGrupoSub),
new KeyValuePair<string, object>("codigoGrupo", codGrupo)
};
transfProduto.GrupoSubFamiliaReference.EntityKey = new EntityKey(
"Entities.GrupoSubFamilia",
entityKeyValues);
transfProduto.dataUltimaAtualizacaoCadastral = System.DateTime.Now;
db.SaveChanges();
i++;
MessageBox.Show("Produtos Alterados com sucesso!");
}
}
catch (Exception ex)
{
MessageBox.Show("Problema ao Atualizar\n"
+ ex.Message,
"Atualizar Produto");
}
}
So pra complementar:
O que é exatamente o IEnumerable?
Um tipo de array?
Porque pelo que eu percebi no C# o array é chamado de list, e contém muito mais funcionalidades.
Ainda sou iniciante, 2 meses de programação profissional (exceto um estágio a 2 anos atrás que fiz em Java).
Antes eu atuava como analista de suporte.
No mais obrigado, se puder tirar minha última dúvida, só pra mim saber qual o mecanismo que eu utilizei, eu agradeceria.
GOSTEI 0
Luiz Maia
03/11/2009
Blz Andre,
Que bom que funcionou. Seguinte o IEnumerable é uma tipo de Array sim, mas uma Interface para métodos não genericos.
Abraços e continuamos a sua disposição para qualquer tipo de duvida, ok?
Att
Luiz Maia
GOSTEI 0