C# 在实体框架核心中的运行时替换实体类
我们在模型中遵循领域驱动的设计原则,将实体和值对象类中的数据和行为结合起来。我们经常需要为客户定制行为。下面是一个简单的示例,其中客户希望更改FullName的格式:C# 在实体框架核心中的运行时替换实体类,c#,entity-framework-core,domain-driven-design,C#,Entity Framework Core,Domain Driven Design,我们在模型中遵循领域驱动的设计原则,将实体和值对象类中的数据和行为结合起来。我们经常需要为客户定制行为。下面是一个简单的示例,其中客户希望更改FullName的格式: public class Person { public string FirstName { get; set; } public string LastName { get; set; } // Standard format is Last, First public virtual stri
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// Standard format is Last, First
public virtual string FullName => $"{LastName}, {FirstName}";
}
public class PersonCustom : Person
{
// Customer wants to see First Last instead
public override string FullName => $"{FirstName} {LastName}";
}
DbSet在DbContext上进行了配置,正如您所期望的那样:
public virtual DbSet<Person> People { get; set; }
公共虚拟数据库集人物{get;set;}
在运行时,需要实例化PersonCustom类来代替Person类。当我们的代码负责实例化实体时,例如添加一个新人时,这不是问题。可以将IoC容器/工厂配置为用Person类替换Person类。但是,在查询数据时,EF负责实例化实体。在查询context.People时,是否有某种方法可以配置或拦截实体创建,以便在运行时用PersonCustom替换Person类
我已经在上面使用了继承,但是如果它起作用的话,我可以实现一个IPerson接口。无论哪种方式,您都可以假定两个类中的接口相同
编辑:这是如何部署的更多信息。。。Person类将是面向所有客户的标准构建的一部分。PersonCustom将进入一个单独的自定义程序集,该程序集只提供给希望更改的客户,因此他们将获得标准构建和自定义程序集。我们不会为整个项目创建单独的构建来适应定制。我的项目中也有类似的任务。有拦截器方法可以做到这一点,但有两个问题:
这可能不是您所要求的,但我希望这会对您有所帮助。我的项目中也有类似的任务。有拦截器方法可以做到这一点,但有两个问题:
这可能不是您要求的,但我希望这会对您有所帮助。如果您的数据存储在基本实体中,我不会尝试使用单独的类。我要做的是使用一个ViewModel,或者你想叫它的任何东西,然后将你的实体映射到这个模型上(手动地或者使用各种各样的AutoMapper)。一般来说,我不建议将您的实体用于数据库交互以外的任何事情。所有逻辑和其他操作都应该在一个单独的类中进行,即使该类恰好是所有属性的1:1副本(如果以后有更改,比必须进行额外迁移或冒其他破坏性更改的风险更容易处理) 一个简单的例子来更好地解释我的想法
//entity
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
//base ViewModel
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
// Standard format is Last, First
public virtual string FullName => $"{LastName}, {FirstName}";
}
//customer specific ViewModel
public class CustomerSpecificModel : PersonModel
{
// Standard format is Last, First
public virtual string FullName => $"{FirstName} {LastName}";
}
//Mapping
public class Mapping
{
public void DoSomeWork()
{
var db = new People();
var defaultPerson = db.People.Select(p => new PersonModel
{
FirstName = p.FirstName,
LastName = p.LastName
};
var customerSpecificModel = db.People.Select(p => new CustomerSpecificModel
{
FirstName = p.FirstName,
LastName = p.LastName
}
Console.WriteLine(defaultPerson.First().FullName); //would return LastName, FirstName
Console.WriteLine(customerSpecificModel.First().FullName); //would return FirstName LastName
}
}
如果数据存储在基本实体中,我不会尝试使用单独的类。我要做的是使用一个ViewModel,或者你想叫它的任何东西,然后将你的实体映射到这个模型上(手动地或者使用各种各样的AutoMapper)。一般来说,我不建议将您的实体用于数据库交互以外的任何事情。所有逻辑和其他操作都应该在一个单独的类中进行,即使该类恰好是所有属性的1:1副本(如果以后有更改,比必须进行额外迁移或冒其他破坏性更改的风险更容易处理) 一个简单的例子来更好地解释我的想法
//entity
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
//base ViewModel
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
// Standard format is Last, First
public virtual string FullName => $"{LastName}, {FirstName}";
}
//customer specific ViewModel
public class CustomerSpecificModel : PersonModel
{
// Standard format is Last, First
public virtual string FullName => $"{FirstName} {LastName}";
}
//Mapping
public class Mapping
{
public void DoSomeWork()
{
var db = new People();
var defaultPerson = db.People.Select(p => new PersonModel
{
FirstName = p.FirstName,
LastName = p.LastName
};
var customerSpecificModel = db.People.Select(p => new CustomerSpecificModel
{
FirstName = p.FirstName,
LastName = p.LastName
}
Console.WriteLine(defaultPerson.First().FullName); //would return LastName, FirstName
Console.WriteLine(customerSpecificModel.First().FullName); //would return FirstName LastName
}
}
基于您提供的一切,我相信为其他客户创建另一个
DbContext
最适合您的场景
下面的代码显示了一个测试,我使用PersonContext
添加Person
实体。然后使用PersonCustomContext
读取相同的实体,但被实例化为PersonCustom
另一个关注点是PersonCustom
键的显式配置,指向在其基本类型Person
中定义的PersonId
键
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Xunit;
public class Tests
{
[Fact]
public void PersonCustomContext_can_get_PersonCustom()
{
var connection = new SqliteConnection("datasource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder()
.UseSqlite(connection)
.Options;
using (var ctx = new PersonContext(options))
ctx.Database.EnsureCreated();
using (var ctx = new PersonContext(options))
{
ctx.Add(new Person { FirstName = "John", LastName = "Doe" });
ctx.SaveChanges();
}
using (var ctx = new PersonCustomContext(options))
{
Assert.Equal("John Doe", ctx.People.Single().FullName);
}
}
}
public class PersonContext : DbContext
{
public PersonContext(DbContextOptions options) : base(options) { }
public DbSet<Person> People { get; set; }
}
public class PersonCustomContext : DbContext
{
public PersonCustomContext(DbContextOptions options) : base(options) { }
public DbSet<PersonCustom> People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonCustom>(builder =>
{
builder.HasKey(p => p.PersonId);
});
}
}
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual string FullName => $"{LastName}, {FirstName}";
}
public class PersonCustom : Person
{
public override string FullName => $"{FirstName} {LastName}";
}
使用Microsoft.Data.Sqlite;
使用Microsoft.EntityFrameworkCore;
使用System.Linq;
使用Xunit;
公开课考试
{
[事实]
public void PersonCustomContext\u可以\u获取\u PersonCustom()
{
var connection=newsqliteconnection(“数据源=:内存:”);
connection.Open();
var options=new DbContextOptionsBuilder()
.UseSqlite(连接)
.选择;
使用(var ctx=新PersonContext(选项))
ctx.Database.recreated();
使用(var ctx=新PersonContext(选项))
{
添加(新的人{FirstName=“John”,LastName=“Doe”});
ctx.SaveChanges();
}
使用(var ctx=newpersoncustomcontext(选项))
{
Assert.Equal(“johndoe”,ctx.People.Single().FullName);
}
}
}
公共类PersonContext:DbContext
{
公共PersonContext(DbContextOptions选项):基本(选项){}
公共数据库集人物{get;set;}
}
公共类PersonCustomContext:DbContext
{
public PersonCustomContext(DbContextOptions选项):基(选项){}
公共数据库集人物{get;set;}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
实体(生成器=>
{
builder.HasKey(p=>p.PersonId);
});
}
}
公共阶层人士
{
公共int PersonId{get;set;}
公共字符串名{get;set;}
公共字符串L