I need a composite unique constraint for my entity's Name property, which is unique per Category (for which it has an FK).
So something like this:
entityTypeBuilder
.HasIndex(i => new { i.Name, i.Category.Id })
.IsUnique();
But this fails when I generate a migration, because of the Category.Id navigation property.
I know I can hardcode the values as strings, but I don't want to lose the static typing.
What options do I have?
As soon as you know the shadow property name, you can use (at least in EF Core 1.1.0) the string based HasIndex method overload
public virtual IndexBuilder HasIndex(params string[] propertyNames)
e.g.
entityTypeBuilder
.HasIndex("Name", "CategoryId")
.IsUnique();
Same for HasAlternateKey:
entityTypeBuilder
.HasAlternateKey("Name", "CategoryId");
Add a foreign key for Category of CategoryId on the entity in question and use that in the index builder instead of the navigation property.
As an extension to Ivan's excellent response:
If you define the foreign key upfront, you can at least control the name association
const string auditForeignKey = "AuditId";
builder.HasOne(e => e.Audit)
.WithMany(e => e.AuditLocations)
.HasForeignKey(auditForeignKey);
builder.HasIndex(auditForeignKey, nameof(AuditLocation.LocationId)).IsUnique();
The detailed answer is only in the comments of the post https://stackoverflow.com/a/40771546/9641435
So I wanted to share the actual code that you could use if you do not want to lose static typing:
entityTypeBuilder
.HasIndex(nameof(MyType.Name), nameof(MyType.CategoryId))
.IsUnique();
or:
entityTypeBuilder
.HasAlternateKey(nameof(MyType.Name), nameof(MyType.CategoryId));
Related
Using EF Core 5.0, I have a PK-less entity (from a SQL view) OrderInfo, which has a column OrderDetailId. I also have an entity DiscountOrder which a PK from the columns OrderDetailId and DiscountId.
I would like to create a navigation property from Order to DiscountOrders. Such as:
public class OrderInfo
{
public int OrderDetailId { get; set; }
public virtual ICollection<DiscountOrder> DiscountOrders { get; set; }
}
public class DiscountOrder
{
public int DiscountId { get; set; }
public int OrderDetailId { get; set; }
}
// For reference, this entity also exists
public class Discount
{
public int DiscountId { get; set; }
}
Obviously, there are no FKs to make use of, but I should be able to create a navigation property anyway.
I think I should be able to do this:
modelBuilder.Entity<OrderInfo>(e =>
{
e.HasNoKey();
e.HasMany(x => x.DiscountOrders)
.WithOne()
.HasPrincipalKey(o => o.OrderDetailId)
.HasForeignKey(pb => pb.OrderDetailId)
.IsRequired(false);
});
But a query on DbSet<OrderInfo> results in a NullReferenceException with the breakpoint landing on the HasMany() line. That said, I don't do anything with the DiscountOrders property, so the exception seems like it would have to be configuration related.
I've looked at answers to similar questions, but most answers use HasOne().WithMany() where as I'd like to keep this definition on OrderInfo since we don't really care about the other direction. How can I correctly set up this mapping?
Keyless entities (entity without key) cannot be principal of a relationship, because there is no key to be referenced by the FK property of the dependent.
Note that by EF Core terminology key is primary key. There are also alternate (unique) keys, but EF Core does not enable them for keyless types.
So basically HasNoKey() disables alternate keys and relationships to that entity. Just the exception is unhandled, hence not user friendly. For instance, if you try to predefine the alternate key referenced by .HasPrincipalKey(o => o.OrderDetailId) in advance
e.HasNoKey();
e.HasAlternateKey(o => o.OrderDetailId);
you'll get much better exception message at the second line
The key {'OrderDetailId'} cannot be added to keyless type 'OrderInfo'.
Shortly, e.HasNoKey(); and `.HasPrincipalKey(o => o.OrderDetailId); are mutually exclusive.
The only way to make it work is to define PK for OrderInfo even though it does not exist in database. In fact if OrderDetailId was supposed to be alternate key, in other words, is unique in the returned set, then you can safely map it as PK
//e.HasNoKey();
e.HasKey(o => o.OrderDetailId);
If it is not unique, then nothing can be done - you cannot map and use navigation property, and will be forced to use manual joins in L2E queries.
Update: EF Core also blocks changing "keyless"-ness once it's been set via fluent API (which has the highest configuration priority). So if you can't remove HasNoKey() fluent call because of it being generated by reverse engineering, you have to resort to metadata API to make it again "normal" entity by setting the IsKeyless property to false before defining the key, e.g.
e.HasNoKey(); // generated by scaffolding
e.Metadata.IsKeyless = false; // <--
e.HasKey(o => o.OrderDetailId); // now this works
I have 2 objects with a FK relationship between them. All of this logic worked fine when i was using .net core 2, but broke when i upgraded to 2.1
public class Parent
{
[Key()]
public Guid ParentGUID {get;set;}
public string SomeValue {get;set;}
[ForeignKey("ReferenceTypeGUID")]
public ReferenceType ReferenceTypeObject {get;set;}
}
public class ReferenceType
{
[Key()]
public Guid ReferenceTypeGUID{get;set;}
public string SomeOtherValue {get;set;}
public virtual ICollection<Parent> ParentGU { get; set; }
}
and then in my dbcontext i have
modelBuilder.Entity<ReferenceType>(entity =>
{
entity.HasKey(e => e.ReferenceTypeGUID);
entity.HasMany(c => c.ParentGU)
.WithOne(e => e.ReferenceTypeObject)
.HasForeignKey(f => f.ParentGUID)
.OnDelete(DeleteBehavior.ClientSetNull);
});
Now in my original code, i didn't have the HasForeignKey line, and i got the following error:
cannot target the primary key because it is not compatible
adding that line fixed that issue, but now i'm getting
Operand type clash: int is incompatible with uniqueidentifier
for some reason, EF is assuming that the database type should be int instead of uniqueidentifier, even though the object is declared as a GUID. how do i fix this issue?
Looks like EF Core 2.1 has introduced a bug when you define a shadow FK property via ForeignKey annotation (frankly I didn't know this is supported at all). It incorrectly assumes int or int? type for that shadow property rather than considering the referenced PK property type.
You might fill an issue in their issue tracker if you wish. But I won't recommend using ForeignKey attribute for that purpose (or data annotations for relationships at all).
You can fix it by adding the following to the Parent entity fluent configuration:
entity.Property<Guid?>("ReferenceTypeGUID");
but this makes sense if you want to configure let say a different database column name for the shadow property.
A better (IMO) way is to remove the ForeignKey attribute and simply use the HasForeignKey overload with string foreignKeyPropertyNames parameter:
entity.HasMany(c => c.ParentGU)
.WithOne(e => e.ReferenceTypeObject)
.HasForeignKey("ReferenceTypeGUID") // <--
.OnDelete(DeleteBehavior.ClientSetNull);
Problem with: Dapper Extensions dbConnection.Get(personId)
I have a model called Person:
Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
In the database I have this table:
data.Persons
Pers_Id
Pers_Name
When I try this without any kind of custom mapping, I get an error "Invalid object name 'Person'."
I believe this is a mapping issue, because when I completely map the model with the prefix 'Pers_', and use 'data.Persons'
Get works.
But is there a way to automatically map with a prefix? The database I'm using has many different tables
with different prefices.
I also have everything already mapped to Entity framework. Is there a possibility of getting the map settings from
Entity DbModelBuilder?
Dapper-Extensions is convention based. For schema, it uses .dbo and for primary key it uses Id. If your tables don't match the convention, you will have to create a custom mapping.
public class MyModelMapper : ClassMapper<MyModel>
{
public MyModelMapper()
{
//use different table name
Table("table_name");
//use a custom schema
Schema("not_dbo_schema");
//have a custom primary key
Map(x => x.ThePrimaryKey).Key(KeyType.Assigned);
//Use a different name property from database column
Map(x=> x.Foo).Column("Bar");
//Ignore this property entirely
Map(x=> x.SecretDataMan).Ignore();
//optional, map all other columns
AutoMap();
}
}
An alternative is to use Dapper and just write your inline queries:
connection.Query("select * from foo.table where myId = {myId}", new {myId})
Update:
Another alternative is to play around with Code Generation and T4 Text Templates
Here is a trivial example
When creating POCO classes that contain collections of primitive types and are persisted by EF Code First, the best advice I have found so far is to create a new class that has an ID plus the primitive type:
Entity Framework and Models with Simple Arrays
If I now have several classes that require properties of type ObservableCollection<string> and replace them with ObservableCollection<EntityString> (where EntityString is a custom type with an Id and a string property), I end up with a table EntityString that has multiple foreign key columns, one for each property of type ObservableCollection<EntityString> across all concrete types with such properties.
This leads to a bloating of mostly-null foreign key columns in the EntityString table.
One approach would be to create a subclass of EntityString and use the Table per Type model for those subclasses. However, that requires making awkward changes to the object model simply to accommodate Entity Framework.
Questions:
Is the encapsulating type the best way to manage Collection<PrimitiveType>?
If so, what are the pro's and con's of allowing multiple (many) foreign key columns vs. creating custom tables per type (at the cost of an awkward model)?
Promoting simple type to entity is one option. If you want to use that new primitive type entity in more relations it is better to completely remove navigation properties from that entity and use independent association (no FK properties).
public class StringEntity
{
public int Id { get; set; }
public string Text { get; set; }
}
and mapping:
modelBuilder.Entity<Foo1>().HasMany(f => f.Strings).WithOptional();
modelBuilder.Entity<Foo2>().HasMany(f => f.Strings).WithOptional();
In database you will get new nullable FK per related principal - there is no way to avoid it except create special StringEntity class per principal (don't use inheritance for that because it affects performance).
There is an alternative:
public class StringEntity
{
public int Id { get; set; }
public List<string> Strings { get; private set; }
public string Text
{
get
{
return String.Join(";", Strings);
}
set
{
Strings = value.Split(";").ToList();
}
}
}
In this case you don't need related entity type (and additional table) but your entity is polluted with additional property Text which is only for persistence.
I have a schema similar to the standard Product / OrderDetails / Order setup. I want to delete a single Product and cascade delete all OrderDetails which reference that product.
Assuming that I've thought this through from the business rules perspective, what's the most elegant way to handle that with Entity Framework 4?
First thing is first:
Is there any reason on delete cascade at the database level won't work?
If that's really not a possibility, you could try the following:
Since ObjectContext doesn't have a DeleteAll style method...you could always implement your own:
public static void DeleteAll(this ObjectContext context,
IEnumerable<Object> records)
{
foreach(Object record in records)
{
context.DeleteObject(record);
}
}
Then you could write something like (probably in a Repository):
context.DeleteAll(context.OrderDetails.Where(od => od.Product == product));
Or, to be a little cleaner:
var toDelete = context.OrderDetails.Where(od => od.Product == product);
context.DeleteAll(toDelete);