我正在开发一个ASP.NET MVC
项目,首先使用C#
, 和EF
代码。
我需要向实体添加动态属性。例如 -
我有一辆汽车作为基础对象。我可以为其添加自定义属性,例如发动机功率、长度、颜色等。
属性可以是布尔值、整数、字符串或选择选项(即,当用户为这些属性输入值时,我必须创建复选框、输入或选择 html 元素)。
属性必须具有自定义验证规则(即,必需、仅数字、仅范围等)。
你们能给我任何线索或方向来完成这个吗?
谢谢
我正在开发一个ASP.NET MVC
项目,首先使用C#
, 和EF
代码。
我需要向实体添加动态属性。例如 -
我有一辆汽车作为基础对象。我可以为其添加自定义属性,例如发动机功率、长度、颜色等。
属性可以是布尔值、整数、字符串或选择选项(即,当用户为这些属性输入值时,我必须创建复选框、输入或选择 html 元素)。
属性必须具有自定义验证规则(即,必需、仅数字、仅范围等)。
你们能给我任何线索或方向来完成这个吗?
谢谢
如果您确实具有动态属性,则您将无法(直接)使用 EF6 执行此操作,因为 EF6 假定使用关系数据库。并且关系数据库需要知道期望哪些列。
现在你有2个选择。
选项 1:在 EF 7 中使用非关系数据库。您可以在此处查看https://msdn.microsoft.com/en-us/magazine/dn890367.aspx,了解有关 EF7 的更多详细信息,但基本上在非关系数据库中您可以存储任何 json blob - 你的动态属性也是如此
选项 2:在对象中使用键值对。并存储这些属性
class KeyValuePair {
int Id {get; set;}
string name {get; set;}
string stringValue {get; set;}
}
class BaseObject {
int Id {get; set;}
list<KeyValuePair> dynamicProperties {get; set;}
}
现在您的汽车可以从这个基础对象继承。您仍然需要完成创建 KeyValuePair 对象的工作。(如果你想存储字符串、整数等,你可以制作不同的 KeyValuePair 类型,每种存储类型一个)
如果您使用这样的动态属性,请注意性能。
更新:
如果要验证这样的动态对象,则要实现 IValidatableObject
所以你得到
class Car: BaseObject, IValidatableObject {
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
/* code to validate your properties here, for example you need at least 1 engine, 4 wheels etc */
yield return ValidationResult.Success;
}
}
您可以动态地在 DB 中创建和使用表,尽管它不是那么简单。
首先,您需要存储有关表的元数据——它们的名称是什么,它们有什么属性,这些属性的类型是什么,等等。
其次,您需要生成实体来访问这些表以及EntityTypeConfiguration
类,如下所示:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class FooTypeConfiguration : EntityTypeConfiguration<Foo>
{
public FooTypeConfiguration()
{
ToTable("Foos");
HasKey(t => t.Id);
Property(t => t.Name).HasMaxLength(200)
.IsRequired();
}
}
您可以在System.Reflection.Emit
. 或者您可以生成 C# 代码并使用System.CodeDom.Compiler
它来编译它(这种方式更简单)。您也可以尝试 Roslyn 编译器(但我没有足够的经验来建议它)。
第三,您需要加载已编译的 DLL 并DbContext
使用modelBuilder.Configurations.AddFromAssembly(...)
.
您可以在程序集中找到所需的类型并使用它来访问数据:
string typeName = ...;
var type = dynamicLoadedAssembly.GetType(typeName);
var set = dbContext.Set(type); // non-generic DB Set
您可以使用System.Reflection
或dynamic
键入来处理这些对象。最后,如果您将生成 C# 代码,您可以生成属性和一些接口的实现,以通过名称访问这些属性:
public interface IAccessorByName : IReadOnlyDictionary<string, object>
{
object this[string name] { get; set; }
}
public Foo : IAccessorByName
{
private static readonly IDictionary<string, Func<Foo, object>> getters = new Dictionary<string, Func<Foo, object>>
{
{ "Id", (foo) => foo.Id },
{ "Name", (foo) => foo.Name },
};
private static readonly IDictionary<string, Action<Foo, object>> setters = new Dictionary<string, Action<Foo, object>>
{
{ "Id", (foo, value) => { foo.Id = (int)value; } },
{ "Name", (foo, value) => { foo.Name = (string)value; } },
};
public int Id { get; set; }
public string Name { get; set; }
public object this[string name]
{
get { return getters[name](this); }
set { setters[name](this, value); }
}
}
使用类似的界面,您可以动态地创建、读取、更新和删除对象:
string typeName = "Foo";
var fooType = dynamicLoadedAssembly.GetType(typeName);
var foo = (IAccessorByName)Activator.CreateInstance(fooType);
foo["Id"] = 1;
foo["Name"] = "Jon Skeet";
var fooSet = dbContext.Set(fooType);
fooSet.Add(foo);
dbContext.SaveChanges();