What you are trying to do is possible in SQL server, according to this article.
However, this is not possible in Entity Framework. If you point your foreign key (FKCatId) to Entity "B", you never define which property you reference in class B. Entity Framework just assumes you will always reference the Key of the entity.
I can, however, think of a workaround. If your database table of 'B' stays the same, you can still map that entity in EF as follows:
class Program
{
static void Main()
{
using (var context = new ProductContext())
{
var cat1 = new Cat {CatId = Guid.NewGuid(), CatName = "MyCat1"};
var cat2 = new Cat {CatId = Guid.NewGuid(), CatName = "MyCat2"};
context.Cats.Add(cat1);
context.Cats.Add(cat2);
context.Products.Add(new Product {Cat = cat1});
context.Products.Add(new Product {Cat = cat2});
context.SaveChanges();
}
}
}
public class ProductContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Cat> Cats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Product>().HasOptional(s => s.Cat).WithMany().HasForeignKey(f => f.FkCatId);
}
}
public class Product
{
public int Id { get; set; }
public Guid? FkCatId { get; set; }
public virtual Cat Cat { get; set; }
}
public class Cat
{
[Key]
public Guid CatId { get; set; }
public string CatName { get; set; }
}
This works flawlessly when your DB looks like this:
CREATE TABLE [dbo].[Cats](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CatId] [uniqueidentifier] NOT NULL,
[CatName] [varchar](max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY CLUSTERED
([Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FkCatId] [uniqueidentifier] NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY CLUSTERED
([Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Cats] FOREIGN KEY([FkCatId])
REFERENCES [dbo].[Cats] ([CatId]);
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_Cats];
In fact, this is what an ORM is supposed to do: map between your code concepts and your DB design, which do not always have to be the same.