Entity framework 5 many to many when an entity is using composite key - many-to-many

im having some problems with EF 5 when i want to map many to many relationships using code first.
This is my scenario:
public class SegFunction
{
public virtual string Idaplication {get;set;}
public virtual string Idfunction {get;set;}
public virtual List<SegRole> Roles { get; set; }
}
public class SegRole
{
public virtual int Idrole { get; set; }
public virtual List<SegFunction> Functions { get; set; }
}
This are my maps:
private void MapSegRole()
{
this.modelBuilder.Entity<SegRole>()
.Map(entity =>
{
entity.ToTable("seg_roles");
});
this.modelBuilder.Entity<SegRole>()
.Property(t => t.Idrole).HasColumnName("id_role");
this.modelBuilder.Entity<SegRole>()
.HasKey(c => c.Idrole);
modelBuilder.Entity<SegRol>()
.HasMany(i => i.Functions)
.WithMany(c => c.Roles)
.Map(
m =>
{
mc.ToTable("seg_role_function");
m.MapRightKey("id_role");
m.MapLeftKey("id_aplication");
m.MapLeftKey("id_function");
});
}
And
private void MapSegFunction()
{
this.modelBuilder.Entity<Segfunction>()
.Map(entity =>
{
entity.ToTable("seg_functions");
});
this.modelBuilder.Entity<Segfunction>()
.Property(t => t.Idfunction).HasColumnName("id_function");
this.modelBuilder.Entity<Segfunction>()
.Property(t => t.Idaplication).HasColumnName("id_aplication");
this.modelBuilder.Entity<Segfuncion>()
.HasKey( d => new { d.Idaplication, d.Idfunction});
this.modelBuilder.Entity<Segfunction>()
.HasMany(t => t.Roles)
.WithMany(r => r.Functions)
.Map(mc =>
{
mc.ToTable("seg_role_function");
mc.MapLeftKey("id_role");
mc.MapRightKey("id_aplication");
mc.MapRightKey("id_function");
});
}
I have three tables
Seg_Role, Seg_Function, Seg_role_function where seg_function has composite primary key (id_aplication, id_function) and seg_role_function has a composite key (id_role, id_aplication, id_function)
i'm getting the following error when i try to get from context:
The specified association foreign key columns 'id_role' are invalid. The number of columns specified must match the number of primary key columns.

MapLeftKey and MapRightKey have a params string[] keyColumnNames parameter, so for composite keys you pass in multiple parameters into those methods instead of calling them twice:
modelBuilder.Entity<SegRol>()
.HasMany(i => i.Functions)
.WithMany(c => c.Roles)
.Map(m =>
{
mc.ToTable("seg_role_function");
m.MapLeftKey("id_role");
m.MapRightKey("id_aplication", "id_function");
});

Related

The instance of entity type 'Decks' cannot be tracked because another instance with the key value '{CardId: 578}' is already being tracked

im new to EF-Core and trying around. I´m stuck on a Problem where i want to Update or Insert Entries in a MySQL Table.
The Model looks like this:
public partial class Decks
{
public int DecklistId { get; set; }
public int CardId { get; set; }
public int Amount { get; set; }
public bool Maindeck { get; set; }
public virtual Cards Card { get; set; }
}
}
The Entity Configuration is the following:
modelBuilder.Entity<Decks>(entity =>
{
entity.HasKey(e => new { e.DecklistId, e.CardId, e.Maindeck })
.HasName("PRIMARY"); ;
entity.HasIndex(e => e.CardId);
entity.ToTable("decks");
entity.Property(e => e.DecklistId).HasColumnName("decklistID");
entity.Property(e => e.CardId).HasColumnName("cardID");
entity.Property(e => e.Maindeck).HasColumnName("maindeck");
entity.Property(e => e.Amount).HasColumnName("amount");
entity.HasOne(e => e.Card)
.WithOne()
.HasPrincipalKey<Decks>(b => b.CardId);
});
Atleast in my Repo im using this Code to find out if the Data should be updated or inserted.
public async Task<StandardResponse> UpdateDeck(List<Decks> deckCards)
{
StandardResponse errors = new StandardResponse { message = null, status = 200 };
try
{
deckCards.ForEach(x =>
{
Decks existingCard = _context.Decks.Find(x.DecklistId,x.CardId, x.Maindeck);
if (existingCard == null)
{
_context.Decks.Add(x); <---- Here occurs the Error
}
else
{
_context.Entry(existingCard).CurrentValues.SetValues(x);
}
_context.SaveChanges();
});
}
catch (DbUpdateException ex)
{
return new StandardResponse { message = ex, status = 501 };
}
return new StandardResponse { message = "Deck updated!", status = 200 };
}
The Problem appears, when i have to Object that have the same DecklistId and the same CardId. Even if the Maindeck property is different i get the error and i cant explain myself why...
Here is an List of Objects i want to Insert/Update.
The first Object will be added. The Second Object is of course not found on the database and at the Add-Statement the error occurs.
[
{
"decklistId": 28,
"cardId": 578,
"amount": 4,
"maindeck": true
},
{
"decklistId": 28,
"cardId": 578,
"amount": 4,
"maindeck": false
}
]
What could it be?
Thanks for helping guys!

Masstransit saga with exceptions

I want to use masstransit saga in my .net core project but I could not find any documentations or (best practice)examples to handling exception.
If I have an error in my consumer,should I publish some failed event and consume that. Actually I don't think it is a good solution. So I want to throw exception and catch in Masstransit Statemachine.
OrderReceivedConsumer.cs
public class OrderReceivedConsumer : IConsumer<IOrderReceivedEvent>
{
public async Task Consume(ConsumeContext<IOrderReceivedEvent> context)
{
var orderCommand = context.Message;
await Console.Out.WriteLineAsync($"Order code: {orderCommand.OrderCode} Order id: {orderCommand.OrderId} is received.");
//do something..
throw new Exception("test");
//await context.Publish<IOrderProcessedEvent>(
// new { CorrelationId = context.Message.CorrelationId, OrderId = orderCommand.OrderId });
}
}
OrderSaga.cs
public class OrderSaga : MassTransitStateMachine<OrderSagaState>
{
public State Received { get; set; }
public State Processed { get; set; }
public Event<IOrderCommand> OrderCommand { get; set; }
public Event<IOrderProcessedEvent> OrderProcessed { get; set; }
public OrderSaga()
{
InstanceState(s => s.CurrentState);
Event(() => OrderCommand,
cec =>
cec.CorrelateBy(state => state.OrderCode, context => context.Message.OrderCode)
.SelectId(selector => Guid.NewGuid()));
Event(() => OrderProcessed, cec => cec.CorrelateById(selector =>
selector.Message.CorrelationId));
Initially(
When(OrderCommand)
.Then(context =>
{
context.Instance.OrderCode = context.Data.OrderCode;
context.Instance.OrderId = context.Data.OrderId;
})
.ThenAsync(
context => Console.Out.WriteLineAsync($"{context.Data.OrderId} order id is received..")
)
.TransitionTo(Received)
.Publish(context => new OrderReceivedEvent(context.Instance))
);
During(Received,
When(OrderProcessed)
.ThenAsync(
context => Console.Out.WriteLineAsync($"{context.Data.OrderId} order id is processed.."))
.Finalize()
);
SetCompletedWhenFinalized();
}
}
Where should I use the Automatonymous library "Catch" method?

Entity Framework Fluent API generating extra columns in sql on related object

I have a pre-existing data model, originally using EF 4 to access. Updating to EF 6, encountering issues with the changes in syntax for configuration, and defining the relationships to query properly.
In this specific case, my foreign key relationship is generating two columns in the SQL, one I defined, one from nowhere...
I have the following two objects - Company and AppUser:
public class Company : EntityBase, IComparable<Company>
{
public string Name { get; set; }
public virtual IList<AppSystem> AppSystems { get; set; }
public virtual IList<AppUser> AppUsers { get; set; }
public string PortalCustomerName { get; set; }
}
public class AppUser : EntityBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual Company Company { get; set; }
public FlowStatus FlowStatus { get; set; }
public virtual IList<AppUserRole> AppUserRoles { get; set; }
}
public abstract class EntityBase
{
/// <summary>
/// The id assigned by the system.
/// </summary>
public virtual int Id { get; set; }
}
Configuration for the two classes:
public class CompanyConfiguration : EntityTypeConfiguration<Company>
public CompanyConfiguration()
{
ToTable("Company");
Property(c => c.Id).HasColumnName("CompanyID");
Property(c => c.Name).HasMaxLength(50).IsRequired();
Property(c => c.PortalCustomerName).HasMaxLength(50).IsRequired();
HasMany(c => c.AppSystems);
HasMany(c => c.AppUsers);
}
}
public class AppUserConfiguration : EntityTypeConfiguration<AppUser>
{
/// <summary>
/// Initializes a new instance of the <see cref="AppUserConfiguration"/> class with default values.
/// </summary>
public AppUserConfiguration()
{
ToTable("AppUser");
Property(u => u.Id).HasColumnName("AppUserId");
Property(u => u.Email).HasMaxLength(256).IsRequired();
Property(u => u.FirstName).HasMaxLength(50);
Property(u => u.LastName).HasMaxLength(50);
Property(u => u.FlowStatus.Value).HasColumnName("FlowStatus");
HasRequired(u => u.Company).WithMany().Map(m => m.MapKey("CompanyID"));
}
}
Data models are:
When I query companies, no problem, I get SQL that matches
SELECT
[Extent1].[CompanyID] AS [CompanyID],
[Extent1].[Name] AS [Name],
[Extent1].[PortalCustomerName] AS [PortalCustomerName]
FROM [dbo].[Company] AS [Extent1]
WHERE N'Joe''s Diner' = [Extent1].[Name]
ORDER BY [Extent1].[Name] ASC
However, when I query AppUsers I'm having a problem with the queries. With the configuration above, I get a correct request for CompanyId but I also get an extra column, Company_Id1:
SELECT
[Extent1].[AppUserId] AS [AppUserId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Email] AS [Email],
[Extent1].[FlowStatus] AS [FlowStatus],
[Extent1].[CompanyID] AS [CompanyID],
**[Extent1].[Company_Id1] AS [Company_Id1]**
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])
But if I remove the mapping and just end the configuration with "HasMany()", the correct map goes away, and Company_Id1 becomes "Company_Id":
SELECT
[Extent1].[AppUserId] AS [AppUserId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Email] AS [Email],
[Extent1].[FlowStatus] AS [FlowStatus],
[Extent1].[Company_Id] AS [Company_Id]
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])
What's wrong with my configuration?
The main flaw of using EntityTypeConfiguration is configuring the relationships. The problem is that relationship usually involves two entities, but needs to be configured (properly) just once. The configuration must reflect exactly the navigation and FK properties present.
In your case you have two confilicting configurations of one and the same relationship:
Company:
HasMany(c => c.AppUsers);
AppUser:
HasRequired(u => u.Company).WithMany().Map(m => m.MapKey("CompanyID"));
Note the parameterless WithMany in the second configuration.
As a rule of thumb, always configure the relationship in one place. Since Has methods require navigation property and With doesn't, do it in the configuration for the entity with the navigation property. If both entity have navigation properties for the relationship, then use either one (but still do it only once).
Applying to your scenario, since both your entities have navigation property, either remove the existing line from Company configuration and use the following in the AppUser configuration:
HasRequired(u => u.Company).WithMany(c => c.AppUsers).Map(m => m.MapKey("CompanyID"));
or remove the existing line from AppUser configuration and use the following in the Company configuration:
HasMany(c => c.AppUsers).WithRequired(u => u.Company).Map(m => m.MapKey("CompanyID"));
Looks like I solved it myself. I modified CompanyConfiguration to:
HasMany(c => c.AppUsers).WithRequired(u=>u.Company).Map(u=>u.MapKey("CompanyId"));
And changed AppUserConfiguration
HasRequired(u => u.Company);
Apparently by moving the mapping definition to the primary object (Company) I've told EF - Company can be used by many users, users must have one company, and identify it as "CompanyId"
SELECT
[Extent1].[AppUserId] AS [AppUserId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Email] AS [Email],
[Extent1].[FlowStatus] AS [FlowStatus],
[Extent1].[CompanyId] AS [CompanyId]
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])

How to use Relation::morphMap() for diffrent class

I am using laravel polymorphic relation.
I have defined two morphTo relations for two purpose.
My question is that ,but when I am defining the key of Relation::morphMap() function array , then my array key is same for one case, so I want to know is there any way by which I can specify that I am defining relation for specific class.
My first relation....
Package.php
public function provider()
{
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function packages()
{
return $this->morphMany(VendorPackage::class, 'map', 'map_type_id', 'map_id');
}
Vendor.php
public function packages()
{
return $this->morphMany(VendorPackage::class, null, 'map_type_id', 'map_id');
}
I want to set the key to compare with map_type_id so I am setting the key in service provider.
Relation::morphMap([
config('evibe.roles.planner') => \Vendor::class,
config('evibe.roles.artist') => \Vendor::class,
config('evibe.roles.venue') => \Venue::class,
], false);
My 2nd morphTo relation
Ticket Booking.php
public function provider()
{
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function bookings()
{
return $this->morphMany(TicketBooking::class,null,'map_type_id','map_id');
}
Decors.php
public function bookings()
{
return $this->morphMany(TicketBooking::class,null,'map_type_id','map_id ');
}
Now again I have to define the morphTo in service provider because I am not using the default Model name.
so my morphTo in service providers became like this.
Relation::morphMap([
config('evibe.roles.planner') => \Vendor::class,
config('evibe.roles.artist') => \Vendor::class,
config('evibe.roles.venue') => \Venue::class,
config('evibe.ticket_type.venues') => \Venue::class,
config('evibe.ticket_type.decors') => \Decor::class
], false);
Now my problem is that key config('evibe.roles.planner') and config('evibe.ticket_type.venues) has the same value 3, so when both things is accessed by the relationship then it is throwing error, because array have same key.
So I want to ask is there any other way to define different morphMap for different relationship.
Lets start by defining the polymorphic relations
First relation....
Package.php
public function provider() {
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function packages() {
// you should provide the relation name, in our exemple its called `provider` as a second parameter
return $this->morphMany(VendorPackage::class, 'provider', 'venues');
}
Vendor.php
public function packages() {
// you should provide the relation name, in our exemple its called `provider` as a second parameter
return $this->morphMany(VendorPackage::class, 'provider', 'vendors');
}
Second Relation
TicketBooking.php
public function provider() {
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function bookings() {
return $this->morphMany(TicketBooking::class, 'provider', 'venues');
}
Decors.php
public function bookings() {
return $this->morphMany(TicketBooking::class, 'provider', 'decors');
}
and register Relation::morphMap as
Relation::morphMap([
'vendors' => \Vendor::class,
'venues' => \Venue::class,
'decors' => \Decor::class
]);

Entity Framework does not update an auto-increment field of a computed key in mySql

I'm trying to save an entity using entityframework, although the EF is not update the auto-increment field
public class Address
{
public int Id { get; set; }
public int DatacenterId { get; set; }
public virtual Datacenter Datacenter { get; set; }
}
public class AddressMapping : EntityTypeConfiguration<Address>
{
public AddressMapping()
{
this.HasKey(k => new { k.Id, k.DatacenterId });
this.HasRequired(itr => itr.Datacenter)
.WithMany()
.HasForeignKey(fk => fk.DatacenterId);
this.Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
using (var context = new MyContext())
{
var address = new Address(); //Initialize and set all values. Include the FK
context.Address.Add(address);
context.SaveChanges();
//address.Id is still 0
}
I profiled the following command from mySql:
INSERT INTO `address`(
`Id`,
`DatacenterId`,
)VALUES(
0,
1,
)
I've changed the order of mapping commands and it is working. I didn't know the EF mapping commands could change the mapping result.
public class AddressMapping : EntityTypeConfiguration<Address>
{
public AddressMapping()
{
this.Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasKey(k => new { k.Id, k.DatacenterId });
this.HasRequired(itr => itr.Datacenter)
.WithMany()
.HasForeignKey(fk => fk.DatacenterId);
}
}
And finally it generated the correct sql command:
INSERT INTO `address`(
`DatacenterId`,
)VALUES(
1,
);
SELECT
`Id`
FROM
`address`
WHERE
row_count() > 0 AND `Id` = last_insert_id() AND `DatacenterId` = 1