How can I configure all properties with a certain name in EF Core 6? - entity-framework-core

In the DbContext, I can do this:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) {
base.ConfigureConventions(configurationBuilder);
configurationBuilder.Properties<string>().HaveMaxLength(10);
}
Which will set every string property of all my entities to have a length of 10, but it doesn't let me limit this by property name.
How do I configure just the string properties named AccessGroup on all entities in my DbContext to have a length of 10?
Like this...
public class Entity1 {
public string SomeProperty { get; set; } // <-- use standard conventions
public string AccessGroup { get; set; } // <-- set to length 10
}
public class Entity2 {
public string SomeOtherProperty { get; set; } // <-- use standard conventions
public string AccessGroup { get; set; } // <-- set to length 10
}

Related

Why does PersistentObjectSpace sometimes return a proxy and sometimes return an object?

The debugger shows me that in the following code
_taxRate =
PersistentObjectSpace.FindObject<TaxRate>(CriteriaOperator.Parse("[TaxCodeId] = ?", TaxCodeId));
var _product2 =
PersistentObjectSpace.FindObject<Product>(CriteriaOperator.Parse("[ItemId] = ?", ItemId));
_taxRate is a poco but _product2 is a proxy
The objects are
[Table("TaxCode")]
[DefaultProperty("TaxCode")]
[ImageName("BO_List")]
public class TaxRate : BasicBo
{
[Key] public short TaxCodeId { get; set; }
[Required]
[RuleRequiredField(DefaultContexts.Save)]
[StringLength(20, ErrorMessage = "The field cannot exceed 20 characters. ")]
public string TaxCode { get; set; }
[Required]
[RuleRequiredField(DefaultContexts.Save)]
public decimal Percentage { get; set; }
public override string ToString()
{
return TaxCode;
}
}
and
[Table("MyExtItem")]
[DefaultProperty("ProductCode")]
[NavigationItem("Config")]
public class Product : BasicBo
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
[Column("Item Number")] public string ProductCode { get; set; }
[MaxLength(10)] public string UnitName { get; set; }
public int? ProductImageId { get; set; }
[ForeignKey("ProductImageId")] public virtual ProductImage ProductImage { get; set; }
[ForeignKey("ItemId")] public virtual ExtMaterialProperty ExtMaterial { get; set; }
}
This is expected behaviour when EF is configured to support lazy loading.
TaxRate holds no references to other entities so EF can return a concrete instance.
Product contains two references to other entities, ProductImage and ExtMaterial.
If I run the code:
var product = context.Products.Single(x => x.ItemId == itemId);
to get a product, EF uses a proxy in order to be prepared for when I try to access something like ProductImage.
var imageName = product.ProductImage.Name;
You can disable the proxies using Configuration.ProxyCreationEnabled on the DbContext. (EF6) This does mean that any references will need to be eager loaded or explicitly loaded, as Lazy Loading will not function without the proxies.

Entity Framework Core multiple relationships to same table

I have a problem with two references to the same table with different columns:
public class MainApplicationContext : DbContext
{
public MainApplicationContext(MainSqlDbContext mainSqlDbContext)
{
MainSqlDbContext = mainSqlDbContext;
this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public DbSet<Organisation> Organisations { get; set; }
public DbSet<OrganisationContact> OrganisationContacts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Organisation>()
.HasKey(t => new { t.OrgId, t.OrgType, });
modelBuilder.Entity<OrganisationContact>().Property(p => p.OcsId).HasValueGenerator<SequenceNumberValueGenerator>().ValueGeneratedOnAdd();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(MainSqlDbContext.Database.GetDbConnection());
base.OnConfiguring(optionsBuilder);
}
private MainSqlDbContext MainSqlDbContext;
}
[SequenceNameAttribute("ORGANISATIONCONTACTS", "web")]
[Table("ORGANISATIONCONTACTS", Schema = "dbo")]
[Serializable]
public partial class OrganisationContact
{
[Column("OCS_ACTIVE")]
[MaxLength(1)]
public string OcsActive { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Key]
[Column("OCS_ID")]
public int OcsId { get; set; }
[Column("OCS_NAME")]
[MaxLength(255)]
public string OcsName { get; set; }
[Column("OCS_ORGANISATION_KEY")]
[RelationshipTableAttribue("ORGANISATIONS", "dbo")]
//Relationships
public int OcsOrganisationKey { get; set; }
[ForeignKey("OcsOrganisationKey")]
public Organisation Organisation { get; set; }
[Column("OCS_TYPE")]
[MaxLength(20)]
[RelationshipTableAttribue("ORGANISATIONS", "dbo")]
// Relationships
public string OcsType { get; set; }
[ForeignKey("OCS_TYPE")]
public Organisation Organisation1 { get; set; }
public OrganisationContact()
{
}
}
[SequenceNameAttribute("ORGANISATIONS", "web")]
[Table("ORGANISATIONS", Schema = "dbo")]
[Serializable]
public partial class Organisation
{
[Column("ORG_EMAIL")]
[MaxLength(255)]
public string OrgEmail { get; set; }
[Range(0, int.MaxValue)]
[Column("ORG_ID")]
public int OrgId { get; set; }
[Required]
[Column("ORG_NAME")]
[MaxLength(255)]
public string OrgName { get; set; }
[Required]
[Column("ORG_TYPE")]
[MaxLength(20)]
public string OrgType { get; set; }
[InverseProperty("Organisation")]
public ICollection<OrganisationContact> OrganisationContacts { get; set; }
[InverseProperty("Organisation1")]
public ICollection<OrganisationContact> ORGANISATIONCONTACTS1 { get; set; }
public Organisation()
{
this.OrganisationContacts = new HashSet<OrganisationContact>();
this.ORGANISATIONCONTACTS1 = new HashSet<OrganisationContact>();
}
}
I get this error:
System.InvalidOperationException: 'The property 'OCS_TYPE' cannot be added to the type 'OrganisationContact' because there was no property type specified and there is no corresponding CLR property or field. To add a shadow state property the property type must be specified.
The core issue here is that you define a composite primary key in table Organisation but you try to use single fields as foreign keys in table OrganisationContact.
If the primary key of the referenced table is composite, the foreign keys referencing it must be composite, as well, consisting of fields of the same number and type:
[Table("ORGANISATIONCONTACTS", Schema = "dbo")]
public partial class OrganisationContact
{
// irrelevant declarations omitted for brevity...
[Column("OCS_ORGANISATION_ORG_ID")]
public int Organisation_OrgId { get; set; }
[Column("OCS_ORGANISATION_ORG_TYPE")]
public string Organisation_OrgType { get; set; }
[ForeignKey(nameof(Organisation_OrgId) + "," + nameof(Organisation_OrgType))]
public Organisation Organisation { get; set; }
[Column("OCS_ORGANISATION1_ORG_ID")]
public int Organisation1_OrgId { get; set; }
[Column("OCS_ORGANISATION1_ORG_TYPE")]
public string Organisation1_OrgType { get; set; }
[ForeignKey(nameof(Organisation1_OrgId) + "," + nameof(Organisation1_OrgType))]
public Organisation Organisation1 { get; set; }
}
[Table("ORGANISATIONS", Schema = "dbo")]
public partial class Organisation
{
// irrelevant declarations omitted for brevity...
[InverseProperty(nameof(OrganisationContact.Organisation))]
public ICollection<OrganisationContact> OrganisationContacts { get; set; }
[InverseProperty(nameof(OrganisationContact.Organisation1))]
public ICollection<OrganisationContact> ORGANISATIONCONTACTS1 { get; set; }
}
Some suggestions:
Please post MCV code. There are some exotic attributes (like RelationshipTableAttribue) and unknown type references (MainSqlDbContext) which has nothing to do with the problem but makes more cumbersome to review the issue.
Try to avoid hardcoded strings as much as possible. The nameof operator has been available for quite a while (since C# 6.0).
The preferred way to configure your DB mappings is fluent API in EF Core. Data annotation attributes are pretty limited in functionality. (E.g. you cannot define a composite primary key using attributes in EF Core.)

Entity Framework fails to get child elements

I have SQLite db and these EF models and context.
Models and Context
public class CardHolder
{
public int CardHolderId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
public string TenantName { get; set; }
public ICollection<AccessCard> AccessCards { get; set; }
}
public class AccessCard
{
public int AccessCardId { get; protected set; }
public CardHolder CardHolder { get; set; }
public DateTime ActivationDate { get; protected set; }
public bool ActivationProcessed { get; set; }
public DateTime? DeactivationDate { get; protected set; }
public string DeactivationReason { get; set; }
public bool DeactivationProcessed { get; set; }
}
public class MyContext : DbContext
{
public DbSet<CardHolder> CardHolders { get; set; }
public DbSet<AccessCard> AccessCards { get; set; }
}
And the Main program
class Program
{
static void Main(string[] args)
{
using (var db = new MyContext())
{
var cardHolders = db.CardHolders.Include("AccessCard").ToList();
}
}
}
Question1: Why do I get this exception
System.InvalidOperationException: 'A specified Include path is not
valid. The EntityType 'SQLiteDemo.Models.CardHolder' does not declare
a navigation property with the name 'AccessCard'.'
If I replace it with
var cardHolders = db.CardHolders.Include("AccessCards").ToList();
I get another error:
SQL logic error no such column: Extent2.CardHolder_CardHolderId
What is wrong with Entity Framework?
Question2: Why cant I use arrow function in Include statement, it doesnt compile at all?
var cardHolders = db.CardHolders.Include(x => x.AccessCards).ToList();
Question3: Why do I need to use Include at all if my ICollection association property AccessCards is NOT virtual - that means eager loading must work by itself!
Why the hell it is so problematic and buggy? Nothing works as it should :(
1 - You have a typo as you have already determined :)
1B - "SQL logic error no such column: Extent2.CardHolder_CardHolderId"
EF isn't finding your FK. You could add it to your AccessCard model:
public int CardHolderId { get; set; }
2 - You need to pull in the LINQ extensions. Make sure you have both of these using statements at the top:
using System.Data.Entity;
using System.Linq;
3 - You, like many others, are misunderstanding lazy loading. Eager loading still requires an Include() to fetch related data. Lazy loading only fetches the relations when you access them.

A specified Include path is not valid. The EntityType 'testDB_KYC3Model.ts_upld_doc' does not declare a navigation property with the name 'Fields' [duplicate]

This question already has answers here:
A specified Include path is not valid. The EntityType does not declare a navigation property with the name *
(5 answers)
Closed 3 years ago.
I am getting below error.
A specified Include path is not valid. The EntityType 'testDB_KYC3Model.ts_upld_doc' does not declare a navigation property with the name 'Fields'.
This is my ts_upld_doc class.
public partial class ts_upld_doc
{
string _template;
public ts_upld_doc()
{
this.tr_upld_content = new HashSet<tr_upld_content>();
}
public List<tr_doc_content> Fields { get; set; }
public int upld_docid { get; set; }
public int usr_createdby { get; set; }
public Nullable<int> upld_clientid { get; set; }
public virtual ICollection<tr_upld_content> tr_upld_content { get; set; }
}
this is my tr_doc_content class
public partial class tr_doc_content
{
public int doc_contentid { get; set; }
public int doc_typeid { get; set; }
public string doc_contenttypelabel { get; set; }
public string doc_ctrltype { get; set; }
public string doc_fieldtype { get; set; }
public Nullable<bool> doc_isrequired { get; set; }
public Nullable<bool> doc_isactive { get; set; }
public virtual tm_doc_type tm_doc_type { get; set; }
}
I have on more class with some functions written in it.
public DbDrivenView(string viewName)
{
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentNullException("viewName", new ArgumentException("View Name cannot be null"));
}
_viewName = viewName;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
ts_upld_doc dataForm = dbContext.ts_upld_doc.Include("Fields").First(f => f.upld_employeename == _viewName);
var sb = new StringBuilder();
var sw = new StringWriter(sb);
using (HtmlTextWriter htmlWriter = new HtmlTextWriter(sw))
{
htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);
foreach (var item in dataForm.Fields)
{
htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);
htmlWriter.WriteEncodedText(item.doc_contenttypelabel);
htmlWriter.AddAttribute(HtmlTextWriterAttribute.Id, item.doc_ctrltype);
htmlWriter.AddAttribute(HtmlTextWriterAttribute.Name, item.doc_ctrltype);
htmlWriter.RenderEndTag();
htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);
}htmlWriter.RenderEndTag();
}
writer.Write(dataForm.Template.Replace("#DataFields", sb.ToString()));
}
when i debug this code I am getting below error Exception Details: System.InvalidOperationException: A specified Include path is not valid. The EntityType 'testDB_KYC3Model.ts_upld_doc' does not declare a navigation property with the name 'Fields' near this line of code.
ts_upld_doc dataForm = dbContext.ts_upld_doc.Include("Fields").First(f => f.upld_employeename == _viewName);
Please help me in sorting out this.
Looks like Entity Framework does not know that "Fields" is a foreign key relation. Try to explicitly expose this relation in the ModelBuilder, e.g.
ModelBuilder.Entity<ts_upld_doc>().HasMany(d => d.Fields).WithRequired();
If lazy loading is enabled, make sure to mark this collection as virtual.

Adding a sub collection of new object and my new object into the database with Entity framework

The method is really simple and I don't see what am I missing...
public int SaveEvent(Data.Models.Event evnt)
{
db.Events.Add(evnt);
db.SaveChanges();
return evnt.EventId;
}
here is the object declaration:
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
public ICollection<EventTag> EventTags { get; set; }
}
The evnt object contains a property name EventTags that contains 6 new elements.
The evnt is inserted in the database but not the EventTag... any idea ? no error nothing. just the EventTag are not added...
public class EventDbContext : DbContext
{
public DbSet<Event> Events { get; set; }
public DbSet<EventTag> EventTags { get; set; }
public DbSet<Tag> Tags { get; set; }
}
Here is a screenshot of the value:
If the EventTags are not being added to the database you may need to manually specify the EntityState for each tag.
public int SaveEvent(Data.Models.Event evnt)
{
foreach(var tag in evnt.EventTags)
{
db.Entry(tag).State = EntityState.Added;
}
db.Events.Add(evnt);
db.SaveChanges();
return evnt.EventId;
}
You might also want to update your class definition and set the EventTags property as virtual.
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
public virtual ICollection<EventTag> EventTags { get; set; }
}
In your screenshot it looks like the tags are loading, but not the Location property on the tags. If that's the case, then make sure to set the Location property to virtual as well.