Unidirectional many-to-many relationship + Entity Framework Core + fluent api - entity-framework-core

I wanted to create unidirectional relation between Author and Skill.
My models are as below:
public class Author: Entity
{
...
public virtual ICollection<Skill> Skills { get; set; }
...
}
public class Skill : Entity
{
...
}
I want to configure it using EF Core's fluent API basic idea is not to create models based on the persistence layer's requirement.
My configuration is as below :
builder.HasMany(r => r.Skills).WithMany("NavigationNotRequired");

Related

Many-many relationships to self - EF 6 port to EF Core 3.1

I have an entity Company that has many-to-many relationships to itself. The idea is to be able to navigate to all associating and associated companies using these relationships.
The following EF 6 code should explain how the relationships were mapped in EF 6.
public class Company {
...
public virtual ICollection<Company> AssociatedFrom { get; set; } = new List<Company>();
public virtual ICollection<Company> AssociatedTo { get; set; } = new List<Company>();
...
}
And this is how relationships were configured
HasMany(company => company.AssociatedTo)
.WithMany(x => x.AssociatedFrom)
.Map(mc =>
{
mc.MapLeftKey("Company_1");
mc.MapRightKey("Company_2");
mc.ToTable("Companies_Companies");
});
Now, the question is how can I create the same relationships in EF Core 3.1, keeping the same data structure as there is already a lot of data in the database we don't want to lose.
Note: EF Core 3.1, doesn't seem to support many-to-many relationships in the way that EF 6 did for .NET Framework.

Self referencing / parent-child relationship one-to-zero or one in Entity Framework Core

I want to create a referencing / parent-child relationship one-to-zero or one in Entity Framework Core. I mean that my entity could have a parent:
public class MyEntity
{
public Guid Id { get; set; }
public Guid? ParentEntityId { get; set; }
public MyEntity ParentEntity { get; set; }
public MyEntity ChildEntity { get; set; }
}
I am trying to configure it via fluent api:
entity.HasOne(x => x.ParentEntity)
.WithOne(x => x.ChildEntity)
.HasForeignKey( .... )
I do not understand what I do have to write in the last line. I am not either sure my entity is correct.
Can anyone help me please?
EDIT: This question does not resolve my problem: Self referencing / parent-child relationship in Entity Framework
My problem is about create the foreign key. This line does not work:
.HasForeignKey(x => x.ParentEntityId)
HasForeignKey expects a string in input.
In a one-to-one relationship you always have to specify the dependent entity type in the HasForeignKey call, i.e. the entity that will contain the foreign key. For a one-to-one relationship between two different classes that makes sense, see the standard EF example. For a self-reference it looks obvious that EF should figure out there's no option. Still, you have to specify the type:
modelBuilder.Entity<MyEntity>()
.HasOne(x => x.ParentEntity)
.WithOne(x => x.ChildEntity)
.HasForeignKey<MyEntity>(c => c.ParentEntityId);

How do you get a web API odatamodelbuilder to work with EF fluent API mappings

I have created a fairly simply domain model using pocos. I have mapped these to an EF DB context using EntityTypeConfiguration<TEnitityType> classes. This all works fine.
I am now trying to create an OData V4 WebAPI controller endpoint using a ODataConventionModelBuilder and this is where things are coming unstuck. It all works fine until it encounters an association that is not convention based. But I cannot find a way to get the ODataBuilder to pick up the mappings from my EntityTypeConfiguration<TEnitityType> classes.
This leaves my with 2 unpalatable options
Decorate my lovely clean pocos with dirty attributes.
Re-map all the non convention based mappings manually using the ODataBuilder
Not sure if code samples will help but here they are anyway, i have simplified the entities for brevity.
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Item>("Items");
config.MapODataServiceRoute(
routeName: "odata",
routePrefix: "odata",
model: builder.GetEdmModel(),
batchHandler: new DefaultODataBatchHandler((GlobalConfiguration.DefaultServer)));
public class Item
{
public Int32 Id { get; set; }
public Int16 ItemTypeId { get; set; }
public virtual ItemType Type { get; set; }
public virtual ICollection<ItemVersion> Versions { get; set; }
public virtual ICollection<ItemTag> Tags { get; set; }
}
The problem comes when it encounters the ItemTags collection, here is an ItemTag:
public class ItemTag
{
public Int32 ItemId { get; set; }
public string Tag { get; set; }
public Item Item { get; set; }
}
Which you can see is not convention based and I have a configuration class for it as follows:
public class ItemTagConfiguration : EntityTypeConfiguration<ItemTag>
{
public ItemTagConfiguration()
{
HasKey(x => new {x.ItemId, x.Tag});
HasRequired(x => x.Item)
.WithMany(y => y.Tags)
.HasForeignKey(x => x.ItemId);
}
}
Does anyone know of a way that I can use these EntityTypeConfiguration files with an ODataBuilder or web API?
EDIT
If found this page which seems to indicate it might be possible with EF 6 which I am using. What I want to do is this
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Dbf>("Dbfs");
// modelBuilder.Configurations.Add(new DbfMap()); <---- NO GOOD - Needs Class from DBContext we only have a model builder :(
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
but the builder does not have a Configurations property.
Two things:
I have read multiple sources now that ward against using lazy loading and serialization; which is basically what OData is; (It even uses the system.runtime.serialization.datacontract and datamember attributes)
I have had more success in explicitly loading from context, and defining navigation properties in the modelbuilder for dbContext. I understand you are looking at customized nav properties, but I am fairly sure these are overriden methods useful for the ODataModelBuilder class (that does not assume much and needs less Entity Framework to work). Where you mentioned using EF already, I imagine that is the direction you will work, and if you do not need to alias your model names, you add an entry for each Set, using convention naming.
EntitySet("routePrefixName")
in building the EdmModel, and it wires up the relationships you made using Fluent previously. If you do have to add extraneous items to the underlying model, you should define each class as an EntityType<>(), only setting the key. EdmBuilder can use mild properties and key association to attach to the EF model in the ODataConventionModelBuilder.
I have wrestled and sought for some time, and there does not seem to be a wealth of information on .Net OData v4 floating around, probably due to the whole force datetimeoffset issue.
Hope that helps somewhat

Entity Framework 6.1 Unidirectional Navigation Property 0 to Many with Fluent API

I'm new to EF and am having issues trying to create a unidirectional navigation association (0 to many) using the Fluent API. Here are simplified versions of the classes:
public partial class Company
{
public int Id { get; set; }
// "Company" is NOT REQUIRED to have any BillingInfo records/objects
public virtual IList<BillingInfo> BillingInfos { get; set; }
}
public partial class BillingInfo
{
public int Id { get; set; }
// A "BillingInfo" requires ONE "Company"
public int Company_Id { get; set; }
}
I'm using EF 6.1 Code First with migrations enabled along with SQL Server 2012.
In my derived EntityTypeConfiguration classes for "Company" and "BillingInfo", I've tried every which way I can think of to achieve:
A Company DOES NOT REQUIRE any BillingInfo records, but MAY HAVE MANY.
A BillingInfo DOES REQUIRE only ONE Company.
Maintain a unidirectional navigation between Company and BillingInfo. (don't want to have a Company navigation property on BillingInfo)
EF Migration creates the NON-NULLABLE Company_Id field in database, WITH a defined ForeignKey constraint.
All the methods I've tried, only gets me partially there. The closest I've come is this (but the only thing missing is the foreignkey constraint isn't created):
class CompanyConfig : EntityTypeConfiguration<Company>
{
public CompanyConfig()
{
this.HasOptional(company => company.BillingInfos)
.WithMany()
.Map(m => m.MapKey("Company_Id"));
}
}
Any ideas???
I think you should use the following code:
private class CompanyMapping : EntityTypeConfiguration<Company>
{
public CompanyMapping()
{
this.HasMany(o => o.BillingInfos).WithOptional().HasForeignKey(fk => fk.Company_Id);
}
}
private class BillingInfoMapping : EntityTypeConfiguration<BillingInfo>
{
public BillingInfoMapping()
{
this.HasOptional(o => o.Company).WithMany(c=>c.BillingInfos).HasForeignKey(fk => fk.Company_Id);
}
}

API Versioning with ASP.NET Web API and Entity Framework

I am developing a REST API using ASP.NET Web API, Code-First Entity Framework 5 and SQL Server 2012 and I need to be able to version the API. I've read a few blog posts and articles about indicating the API version either in the URI or in a custom HTTP header and using a custom IHttpControllerSelector to select different ApiControllers based on the indicated version. This all makes sense.
What I'm struggling to figure out is how to manage the affects of versioning beyond the Web API layer, specifically in Entity Framework. How do I go about evolving my DbContext without breaking older versions of the API? Can I version the DbContext as well? And if so, how?
What I ended up doing was combining the Repository Pattern with Pablo's answer. The gist of it is that my EF models are versioned, I use EF Code-First Migrations to migrate the database to the new versions of the models, my DbContext always works with the latest version of the models, I developed a number of concrete repositories that each implement the IRepository<TItem> interface below.
public interface IRepository<TItem> : IQueryable<TItem>, ICollection<TItem>, IDisposable
where TItem : class
{
void Update(TItem item);
void SaveChanges();
}
One implementation of IRepository<TItem> is DbRepository<TItem> which wraps the entity framework code used to talk to the database.
public class DbRepository<TItem> : IRepository<TItem>
where TItem : class
{
private MyDbContext _db;
public DbRepository()
{
_db = new MyDbContext();
}
// Implementation of IRepository<TItem> methods
}
Another implementation of IRepository<TItem> is TypeConversionRepository<TExternal,TInternal> which is an abstract class that facilitates converting from one model type to another.
public abstract class TypeConversionRepository<TExternal, TInternal> : IRepository<TExternal>
where TExternal : class
where TInternal : class
{
protected IRepository<TInternal> InternalRepository { get; set; }
protected abstract TInternal ConvertInbound(TExternal externalItem);
protected abstract TExternal ConvertOutbound(TInternal internalItem);
// Implementation of IRepository<TItem> methods
}
Methods that return models or accept models as parameters use ConvertInbound() and ConvertOutbound() to convert models of type TExternal to TInternal and vice versa. Therefore, given the following 2 versions of MyModel, we can write 2 versions of MyModelRepository; version 2 can talk directly to the database while version 1 will need to convert from version 2 back to version 1.
namespace Models.v1
{
public class MyModel
{
public int Id { get; set; }
public string MyProperty { get; set; }
}
public class MyModelRepository : TypeConversionRepository<Models.v1.MyModel,Models.v2.MyModel>
{
MyModelRepository()
{
this.InternalRepository = new Models.v2.MyModelRepository();
}
protected override TInternal ConvertInbound(TExternal externalItem)
{
return new Models.v2.MyModel
{
Id = externalItem.Id,
MyNewProperty = externalItem.MyProperty
};
}
protected override TExternal ConvertOutbound(TInternal internalItem)
{
return new Models.v1.MyModel
{
Id = internalItem.Id,
MyProperty = internalItem.MyNewProperty
};
}
}
}
namespace Models.v2
{
public class MyModel
{
public int Id { get; set; }
public string MyNewProperty { get; set; }
}
public class MyModelRepository : DbRepository<MyModel>
{
}
}
Now the v1 ApiController can use the v1 MyModelRepository, the v2 ApiController can use the v2 MyModelRepository, but in the end all requests utilize a database that has been migrated to v2.
I think it is a good practice to evolve the Web API and the underline DB model separately (or EF model). That means a DTO model for the Web API, which is mapped to the EF Model in the Web API. That layer of indirection will give you the chance to make changes that perhaps only affects the Web API or the EF model. In addition, a new version in Web API might not impact directly in existing EF model. For example, a new version of the Web API that uses a completely different set of tables.
Regards,
Pablo.