Em tempo:
Quando executei:
PM> add-migration criaBandeiraCartaoCreditoVendaCartao -Context EntidadesContext
Obtive:
System.InvalidOperationException: The navigation 'VendaCartaoCreditos' on entity type 'lojaComEntity.Entidades.CartaoCredito' has not been added to the model, or ignored, or target entityType ignored.
at Microsoft.Data.Entity.Metadata.Conventions.Internal.PropertyMappingValidationConvention.Apply(InternalModelBuilder modelBuilder)
at Microsoft.Data.Entity.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
at Microsoft.Data.Entity.Metadata.Internal.InternalModelBuilder.Validate()
at Microsoft.Data.Entity.ModelBuilder.Validate()
at Microsoft.Data.Entity.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.Data.Entity.Infrastructure.ModelSource.<>cDisplayClass8_0.b0(Type k)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func
2 valueFactory)
at Microsoft.Data.Entity.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.Data.Entity.Internal.DbContextServices.CreateModel()
at Microsoft.Data.Entity.Internal.LazyRef1.get_Value()
at Microsoft.Data.Entity.Internal.DbContextServices.get_Model()
at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_5(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetService[T](IServiceProvider provider)
at Microsoft.Data.Entity.Design.Internal.DesignTimeServicesBuilder.<>c__DisplayClass7_0.<ConfigureContextServices>b__8(IServiceProvider _)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.Data.Entity.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
at Microsoft.Data.Entity.Design.OperationExecutor.<AddMigrationImpl>d__7.MoveNext()
at System.Linq.Buffer
1..ctor(IEnumerable1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable
1 source)
at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.<>cDisplayClass4_0`1.b0()
at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.Execute(Action action)
The navigation 'VendaCartaoCreditos' on entity type 'lojaComEntity.Entidades.CartaoCredito' has not been added to the model, or ignored, or target entityType ignored.
Alterei a classe VendaCaraoCredito, retirando o IList e descomentei o ID (achei que não ia ser necessário, mas foi). Ficando:
public class VendaCartaoCredito
{
public int ID { get; set; }
public int VendaID { get; set; }
public virtual Venda Venda { get; set; }
public int CartaoCreditoID { get; set; }
public virtual CartaoCredito CartaoCredito { get; set; }
}
Executei o comando novamente:
PM> add-migration criaBandeiraCartaoCreditoVendaCartao -Context EntidadesContext
e deu certo:
To undo this action, use Remove-Migration.
Mas, quando fui aplicar o update:
PM> update-database -Context EntidadesContext
Applying migration '20170302013406_criaBandeiraCartaoCreditoVendaCartao'.
System.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_Venda_Usuario_ClienteID' on table 'Venda' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.<>c.<ExecuteNonQuery>b__13_0(DbCommand cmd, IRelationalConnection con)
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.Execute[T](IRelationalConnection connection, Func
3 action, String executeMethod, Boolean openConnection, Boolean closeConnection)
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.ExecuteNonQuery(IRelationalConnection connection, Boolean manageConnection)
at Microsoft.Data.Entity.Storage.RelationalCommandExtensions.ExecuteNonQuery(IEnumerable1 commands, IRelationalConnection connection)
at Microsoft.Data.Entity.Migrations.Internal.Migrator.Execute(IEnumerable
1 relationalCommands)
at Microsoft.Data.Entity.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.Data.Entity.Design.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.Data.Entity.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
at Microsoft.Data.Entity.Design.OperationExecutor.UpdateDatabase.<>cDisplayClass0_1.<.ctor>b0()
at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.Execute(Action action)
ClientConnectionId:d39dde93-fcea-468c-a200-2b42214ef517
Error Number:1785,State:0,Class:16
-- essas linhas estão em vermelho--
Introducing FOREIGN KEY constraint 'FK_Venda_Usuario_ClienteID' on table 'Venda' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
-- essas linhas estão em vermelho --
Na última linha do Up, comentei o onDelete e alterei para onUpdate (espero que tenha feito certo):
migrationBuilder.AddForeignKey(
name: "FK_Venda_Usuario_ClienteID",
table: "Venda",
column: "ClienteID",
principalTable: "Usuario",
principalColumn: "Id",
//onDelete: ReferentialAction.Cascade);
onUpdate: ReferentialAction.Cascade);
Ao executar o comando, obtive sucesso na execução:
PM> update-database -Context EntidadesContext
Applying migration '20170302013406_criaBandeiraCartaoCreditoVendaCartao'.
Done.
PM>
Quando fui olhar as tabelas, verifiquei que na BandeiraCartaoCredito, foi criado o CartaoCreditoID. Errei na programação.
1) Fiz a alteração na Classe BandeiraCartaoCredito retirando o CartaoCreditoID:
public class BandeiraCartaoCredito
{
public int ID { get; set; }
public string Nome { get; set; }
//public int CartaoCreditoID { get; set; }
public virtual IList CartaoCredito { get; set; }
}
2) Executei o comando:
PM> add-migration alteraBandeiraCartaoCredito -Context EntidadesContext
An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.
To undo this action, use Remove-Migration.
3) Mas ao executar o:
PM> update-database -Context EntidadesContext
Obtive o resultado:
Applying migration '20170302020806_alteraBandeiraCartaoCredito'.
System.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_VendaCartaoCredito_Venda_VendaID' on table 'VendaCartaoCredito' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.<>c.<ExecuteNonQuery>b__13_0(DbCommand cmd, IRelationalConnection con)
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.Execute[T](IRelationalConnection connection, Func
3 action, String executeMethod, Boolean openConnection, Boolean closeConnection)
at Microsoft.Data.Entity.Storage.Internal.RelationalCommand.ExecuteNonQuery(IRelationalConnection connection, Boolean manageConnection)
at Microsoft.Data.Entity.Storage.RelationalCommandExtensions.ExecuteNonQuery(IEnumerable1 commands, IRelationalConnection connection)
at Microsoft.Data.Entity.Migrations.Internal.Migrator.Execute(IEnumerable
1 relationalCommands)
at Microsoft.Data.Entity.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.Data.Entity.Design.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.Data.Entity.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
at Microsoft.Data.Entity.Design.OperationExecutor.UpdateDatabase.<>cDisplayClass0_1.<.ctor>b0()
at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.Execute(Action action)
ClientConnectionId:d1ad8b70-86d5-4c95-8a74-63564a3c8441
Error Number:1785,State:0,Class:16
-- Essas últimas linhas estão em vermelho --
Introducing FOREIGN KEY constraint 'FK_VendaCartaoCredito_Venda_VendaID' on table 'VendaCartaoCredito' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
-- Essas últimas linhas estão em vermelho --
4) Executei o mesmo tipo de alteração que havia feito em Vendas (alterei o onDelete para onUpdate).
migrationBuilder.AddForeignKey(
name: "FK_VendaCartaoCredito_Venda_VendaID",
table: "VendaCartaoCredito",
column: "VendaID",
principalTable: "Venda",
principalColumn: "ID",
//onDelete: ReferentialAction.Cascade);
onUpdate: ReferentialAction.Cascade);
5) Executei o comando e deu tudo certo (graças a Deus!!!)
PM> update-database -Context EntidadesContext
Applying migration '20170302020806_alteraBandeiraCartaoCredito'.
Done.
PM>
Agora, fiz o teste da venda com dois cartões de créditos:
// Teste da minha implementação do Cartão de Crédito
EntidadesContext contexto = new EntidadesContext();
UsuarioDAO dao = new UsuarioDAO();
Usuario freedy = dao.BuscaPorId(6);
Venda vendas = new Venda()
{
Cliente = freedy
};
contexto.Vendas.Add(vendas);
Produto p = contexto.Produtos.FirstOrDefault(prod => prod.ID == 1);
Produto p2 = contexto.Produtos.FirstOrDefault(prod => prod.ID == 2);
ProdutoVenda pv = new ProdutoVenda()
{
Produtos = p,
VendaID = vendas.ID
};
contexto.ProdutoVenda.Add(pv);
ProdutoVenda pv2 = new ProdutoVenda()
{
Produtos = p2,
VendaID = vendas.ID
};
contexto.ProdutoVenda.Add(pv2);
BandeiraCartaoCredito b = new BandeiraCartaoCredito()
{
Nome = "Credicar"
};
contexto.Bandeiras.Add(b);
BandeiraCartaoCredito b2 = new BandeiraCartaoCredito()
{
Nome = "Visa"
};
contexto.Bandeiras.Add(b2);
CartaoCredito c = new CartaoCredito()
{
BandeiraID = b.ID,
Nome = "Freedy Carvalho",
ClienteID = freedy.Id,
Numero = "123456789000",
MesValidade = 12,
AnoValidade = 2023,
CodVerificacao = 165
};
contexto.CartaoCredito.Add(c);
CartaoCredito c2 = new CartaoCredito()
{
BandeiraID = b2.ID,
Nome = "Freedy Carvalho",
ClienteID = freedy.Id,
Numero = "000100020003",
MesValidade = 01,
AnoValidade = 2025,
CodVerificacao = 132
};
contexto.CartaoCredito.Add(c2);
VendaCartaoCredito vc = new VendaCartaoCredito()
{
CartaoCreditoID = c.ID,
VendaID = vendas.ID
};
contexto.VendaCartaoCreditos.Add(vc);
VendaCartaoCredito vc2 = new VendaCartaoCredito()
{
CartaoCreditoID = c2.ID,
VendaID = vendas.ID
};
contexto.VendaCartaoCreditos.Add(vc2);
contexto.SaveChanges();
Verifiquei nas tabelas e todos os dados foram incluídos com sucesso, mas as minhas dúvidas são:
1) Foi uma implementação adequada? Poderia ter feito algo melhor?
2) Na tabela Venda, temos somente o campo ID e ClienteID. Mas não sei se essa venda foi paga com dinheiro ou cartão de crédito. Não seria interessante colocar um campo de forma de pagamento para evitar joins? O que sugere nesse caso? Crio a classe FormaPagamentoVenda?
Ainda não fiz o resgate das informações. Mas farei amanhã e gostaria de contar com a sua opinião.
Obrigado pela atenção.