EF Core - Fluent API many-to-many self reference - ef-fluent-api

I am attempting to configure 2 classes fluently.
public class Company
{
[Key]
public int Id {get; set; }
public string Name { get; set; }
public List<CompanyOwnership> OwnedBy { get; set; }
}
public class CompanyOwnership
{
public static void Configure(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CompanyOwnership>()
.HasOne(cpo => cpo.OwnedCompany)
.WithMany(cp => cp.OwnedBy)
.HasForeignKey(cpo => cpo.OwnedCompanyId);
modelBuilder.Entity<CompanyOwnership>()
.HasOne(cpo => cpo.OwningCompany)
.WithMany()
.HasForeignKey(cpo => cpo.OwningCompanyId);
}
[Key]
public int Id {get; set; }
public int OwnedCompanyId { get; set; }
public Company OwnedCompany { get; set; }
public int OwningCompanyId { get; set; }
public Company OwningCompany { get; set; }
public decimal Percentage { get; set; }
}
The above code will result in an error:
InvalidOperationException: Unable to determine the relationship
represented by navigation property 'Company.OwnedBy' of type
'List<CompanyOwnership>'. Either manually configure the relationship,
or ignore this property using the '[NotMapped]' attribute or by using
'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Could I get some input about why the above setup is not enough?
Thank you,

Nvm,
it turned out that I forgot to call Configure(...).
It is working fine now.

Related

EF Core Sort a list by related data

I implemented objects like below in ASP.NET Core 3.1:
public class Content: BaseModel
{
public int ContentId { get; set; }
public virtual List<ContentCBlock> ContentCBlocks { get; set; } = new List<ContentCBlock>();
}
public class CBlock : BaseModel
{
public int CBlockId { get; set; }
public virtual List<ContentCBlock> ContentCBlocks { get; set; } = new List<ContentCBlock>();
}
public class ContentCBlock
{
[Key]
public int ContentId { get; set; }
public virtual Content Content { get; set; }
[Key]
public int CBlockId { get; set; }
public virtual CBlock CBlock { get; set; }
public int DisplayOrder { get; set; }
}
and DBContext
modelBuilder.Entity<ContentCBlock>().HasKey(t => new { t.ContentId, t.CBlockId });
modelBuilder.Entity<ContentCBlock>()
.HasOne(c => c.CBlock)
.WithMany(c => c.ContentCBlocks)
.HasForeignKey(cc => cc.CBlockId);
modelBuilder.Entity<ContentCBlock>()
.HasOne(c => c.Content)
.WithMany(c => c.ContentCBlocks)
.HasForeignKey(cc => cc.ContentId);
DBSets:
public DbSet<ContentCBlock> ContentCBlocks { get; set; }
public DbSet<Content> Contents { get; set; }
public DbSet<CBlock> CBlocks { get; set; }
Everything works fine, relations, lazyLoading, ... everything.
The question is How I can sort a List of Contents by ContentCBlock.DisplayOrder
P.S.: I cannot change classes
Hope this will help you.
_context.Contents.OrderBy(c => c.ContentCBlocks.
OrderBy(c => c.DisplayOrder).Select(c => c.DisplayOrder).FirstOrDefault());

I want no Parent entity or foreign key on the Grandchild entity

I have three entities, BaseEntity, Child and GrandChild, that are predictably linked together.
It goes like this:
public class BaseEntity
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public string ChildId { get; set; }
public virtual BaseEntity Parent { get; set; }
public virtual List<GrandChild> GrandChildren { get; set; }
}
public class GrandChild
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public string ChildId { get; set; }
public string GrandChildId { get; set; }
public virtual Child ParentChild { get; set; }
}
public class BaseContext : DbContext
{
protected override void OnModelCreating(ModelBuilder p_mbuModel)
{
p_mbuModel.Entity<BaseEntity>().ToTable("T_BaseEntity");
p_mbuModel.Entity<BaseEntity>().HasKey(t => new { t.CompanyId, t.BaseEntityId });
p_mbuModel.Entity<Child>().ToTable("T_Child");
p_mbuModel.Entity<Child>().HasKey(t => new { t.CompanyId, t.BaseEntityId, t.ChildId });
p_mbuModel.Entity<Child>().HasOne(c => c.Parent).
WithMany(p => p.Children).
HasForeignKey(c => new { c.CompanyId, c.BaseEntityId }).
HasConstraintName("FK_Child_BaseEntity");
p_mbuModel.Entity<GrandChild>().ToTable("T_GrandChild");
p_mbuModel.Entity<GrandChild>().HasKey(t => new { t.CompanyId, t.BaseEntityId, t.ChildId, t.GrandChildId });
p_mbuModel.Entity<GrandChild>().HasOne(gc => gc.ParentChild).
WithMany(c => c.GrandChildren).
HasForeignKey(gc => new { gc.CompanyId, gc.BaseEntityId, gc.ChildId }).
HasConstraintName("FK_GrandChild_Child");
}
}
You'll note that it features no direct link from GrandChild to BaseEntity and no foreign key between GrandChild and BaseEntity. Which is consistent. I don't want that direct link. (If nothing else, it could lead to unpleasantness with cascading deletions.)
Still, when I launch Add-Migration, I get the following:
table.ForeignKey(
name: "FK_T_GrandChild_T_BaseEntity_BaseEntityId",
columns: x => { x.CompanyId, x.BaseEntityId },
principalTable: "T_BaseEntity",
principalColumns: new { "CompanyId", "BaseEntityId" },
onDelete: ReferentialAction.Cascade);
The very thing that I have tried to keep out. (On migrating, it creates the foreign key constraint that I don't want, and with that unwieldy name to boot.)
I have tried adding
    p_mbuModel.Entity<GrandChild>().Ignore(t => new { t.CompanyId, t.BaseEntityId });
which raises an exception to the tune of
The expression 't => new <>f__AnonymousType10`2(CompanyId = t.CompanyId, BaseEntityId = t.BaseEntityId)' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'.
(I guess that's to be expected; that was a long shot on my behalf.)
I could add a GrandParent property to GrandChild and use Ignore() on it, but that would require me to create the very link I want to hide.
I want no link between BaseEntity and GrandChild, neither as entities nor as database tables.
How can I achieve this?
... I'm embarrassed to admit this. When I posted the question, I trimmed the irrelevant parts of my classes. And I didn't notice that BaseEntity included this line:
public virtual List<GrandChild> GrandChildren { get; set; }
I removed it and launched Add-Migration again. No link appeared between BaseEntity and GrandChild.
Apologies to any and all who tried to make sense of this incomplete riddle.

Sitecore Glass Mapper ObjectToSwitchTo null reference in page editor

I have the following structure as Template in visual studio :
Under a page, i have one or more link root
[SitecoreType(TemplateId = "{4AAA9A10-36C2-484F-A648-2BEF349F0052}", AutoMap = true)]
public class LinkRoot : IBaseTemplate
{
[SitecoreChildren(InferType = true)]
public virtual IEnumerable<LinkItem> Children { get; set; }
[SitecoreInfo(SitecoreInfoType.TemplateId)]
public virtual Guid TemplateId { get; set; }
public Guid Id { get; set; }
public string Language { get; set; }
public ItemUri Uri { get; set; }
public int Version { get; private set; }
}
Under the link root i've LinkItems
[SitecoreType(AutoMap = true)]
public class LinkItem : IBaseTemplate
{
[SitecoreField("Link Name")]
public virtual string LinkName { get; set; }
[SitecoreField("Link")]
public virtual Link Link { get; set; }
public Guid Id { get; set; }
public string Language { get; set; }
public ItemUri Uri { get; set; }
public int Version { get; private set; }
}
I display those items in a view like that :
#foreach (var link in Model.Children.Where(o => o.TemplateId.Equals(TemplateIDs.LinksRoot.Guid)))
{
foreach (var linkChildren in link.Children)
{
using (BeginRenderLink(linkChildren, x => x.Link, isEditable: true))
{
#Editable(linkChildren, x => x.LinkName)
}
}
}
It works great, i can see my links with the good name etc, but when i go to the page editor i got this error :
Value cannot be null. Parameter name: objectToSwitchTo
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
at Sitecore.Common.Switcher2.Enter(TValue objectToSwitchTo)
at Sitecore.Data.Items.ContextItemSwitcher..ctor(Item item)
at Glass.Mapper.Sc.GlassHtml.MakeEditable[T](Expression1 field, Expression`1 standardOutput, T model, Object parameters, Context context, Database database, TextWriter writer)
Does someone already experienced that or have an idea why i have this error ?
Thanks
I think it means that Glass can't resolve the template of the LinkItem model.
Instead of:
[SitecoreType(AutoMap = true)]
public class LinkItem : IBaseTemplate
Try to explicitly define the template ID:
[SitecoreType(TemplateId = "{your-template-guid}", AutoMap = true)]
public class LinkItem : IBaseTemplate
I think this might be a bug, can you log it on github as an issue.

Unable to get the correct regex pattern to change a property into a value setter

Assuming I have some object like, with LOTS of properties:
public class SomeObject
{
public SomeOtherObject1 Property1 { get; set; }
public SomeOtherObject2 Property2 { get; set; }
public SomeOtherObject3 Property3 { get; set; }
public SomeOtherObject4 Property4 { get; set; }
public SomeOtherObject5 Property5 { get; set; }
public SomeOtherObject6 Property6 { get; set; }
}
It would be really cool if I could create a constructor and copy the properties into the constructor...
public class SomeObject
{
public SomeObject
{
public SomeOtherObject1 Property1 { get; set; }
public SomeOtherObject2 Property2 { get; set; }
public SomeOtherObject3 Property3 { get; set; }
public SomeOtherObject4 Property4 { get; set; }
public SomeOtherObject5 Property5 { get; set; }
public SomeOtherObject6 Property6 { get; set; }
}
public SomeOtherObject1 Property1 { get; set; }
public SomeOtherObject2 Property2 { get; set; }
public SomeOtherObject3 Property3 { get; set; }
public SomeOtherObject4 Property4 { get; set; }
public SomeOtherObject5 Property5 { get; set; }
public SomeOtherObject6 Property6 { get; set; }
}
And use Visual Studio's Find And Replace with Regex to change the highlighted lines in the constructor from:
public SomeOtherObject1 Property1 { get; set; }
public SomeOtherObject2 Property2 { get; set; }
public SomeOtherObject3 Property3 { get; set; }
public SomeOtherObject4 Property4 { get; set; }
public SomeOtherObject5 Property5 { get; set; }
public SomeOtherObject6 Property6 { get; set; }
to:
this.Property1 = new SomeOtherObject1();
this.Property2 = new SomeOtherObject2();
this.Property3 = new SomeOtherObject3();
this.Property4 = new SomeOtherObject4();
this.Property5 = new SomeOtherObject5();
this.Property6 = new SomeOtherObject6();
First I tried:
public\s{:i}\s{:i}\s{\sget;\sset;\s}
this.\2 = new \1();
Then I thought maybe it was a line issue, so I tried:
^\s*public\s{:i}\s{:i}\s{\sget;\sset;\s}.$
this.\2 = new \1();
Anyone else have any thought on how to get this to work?
You need to escape the {} around get; set;. Also, I've used :b instead of \s and allowed for more than one. Here:
public:b+{:i}:b+{:i}:b*\{:b*get;:b*set;:b*\}
And as you wrote:
this.\2 = new \1();

Microsoft Moles : circular reference error?

I've the following Entity Framework entities:
public class Country
{
public long Id { get; set; }
public string Code { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
public long Id { get; set; }
public long? Country_Id { get; set; }
public Country HomeCountry { get; set; }
}
Moles has generated MPerson and MCountry stub classes.
Now I do want to stub the set of the Country_Id:
MPerson.AllInstances.Country_IdSetNullableOfInt64 = (Person instance, long? id) =>
{
// Do something
// Set the Country_Id to the provided id
// This will trigger this same method again and again. How to avoid this ?
instance.Country_Id = id;
};
This post gives the answer:
MolesContext.ExecuteWithoutMoles(() => instance.Country_Id = id);