好吧,我试着把它包裹在心里,没有运气;无法找到更好的解决方案,所以我很乐意使用一些帮助/想法。
基本上,我有一个泛型方法,它接收一个businessobject作为泛型参数,它应该实例化一个EntityFramework实体并返回它。 businessobject名称和实体框架名称之间存在命名约定(即,如果BO被命名为'UsersBo',则EF实体被命名为'Users'),并且该约定在任何地方都被强制执行。
我的代码如下所示:
public dynamic CreateEntity<T>()
{
switch (typeof(T).Name)
{
case "UsersBo":
return new Users();
case "RolesBo":
return new Roles();
case "AppObjectsBo":
return new AppObjects();
default:
break;
}
return null;
}
我有两个问题:
鉴于BO和EF实体位于不同的DLL(BLL.dll vs DAL.dll)中,如何在没有丑陋的切换命令的情况下实例化它?尝试使用Activator.CreateInstance,它给了我关于“完全限定名称”的错误
有没有办法 NOT 使用动态对象?现在我正在使用动态因为我不知道将实例化哪个具体类。
谢谢
================================ 稍后编辑:
这是实际的代码:
public T GetById(Guid id)
{
T entityBo = new T();
var entity = new EntityFactory().CreateEntity<T>();
var repository = new EntityFactory().CreateRepository<T>();
try
{
entity = repository.GetById(id);
if (entity != null)
{
Mapper.Map(entity, entityBo);
}
}
catch (Exception ex)
{
entityBo = default(T);
throw ex.GetBaseException();
}
return entityBo;
}
===================== 我有一个与业务对象一起使用的通用服务类。当然,这些必须映射到相应的EF对象。
如果我使用UsersBo作为T调用上述方法,代码将变为:
public UsersBo GetById(Guid id)
{
UsersBo entityBo = new UsersBo(); // no problem here
var entity = new EntityFactory().CreateEntity<T>(); // That's the problem line: T is UsersBo, but I need a new Users (EF entity) in order to do the mapping later
var repository = new EntityFactory().CreateRepository<T>();
try
{
entity = repository.GetById(id); // this one returns the EF entity
if (entity != null)
{
Mapper.Map(entity, entityBo);
}
}
catch (Exception ex)
{
entityBo = default(T);
throw ex.GetBaseException();
}
return entityBo;
}
希望现在更有意义。我对任何建议持开放态度,也许我已经完全离开了,并且有更好的方法。
谢谢你们。
=============
睡过头了。一流的答案,已经改变了代码,就像一个魅力。谢谢你们,伙计们,你们非常乐于助人。
答案 0 :(得分:4)
Activator.CreateInstance是正确的,但您需要指定类的完全限定名称,在这种情况下是这样的:
string className = T.GetType().Name.Replace("Bo", string.Empty);
var fullClassName = string.Concat("Namespace.", className, ",BLL.dll");
object obj = Activator.CreateInstance("BLL.dll", fullClassName);
return obj;
类的完全限定名称包含命名空间的完整路径。在CreateInstance方法中,您还需要指定定义类的DLL名称。请替换“命名空间”。与实际的命名空间。
答案 1 :(得分:4)
你可以这样做,但我仍然不喜欢整体设计,因为你用关键字dynamic
放弃了类型安全。最好使用传入的类型约束来获取要在调用代码中使用的确切类型。
// personally i would change dynamic to object
// calling code could always cast to dynamic if it needed to
public dynamic CreateEntity<T>()
{
var bllName = typeof(T).Name;
var efName = bllName.Substring(0, bllName.Length - 2); // removes "Bo"
var className = string.Concat("Namespace.", efName, ", EfAssembly.dll");
var efType = Type.GetType(className);
return efType != null ? Activator.CreateInstance(efType) : null;
}
这个(参见下面的代码)将是我的偏好,这里T
将是您想要在调用代码中使用的实际EF实体,并且与您的BLL类型完全无关。或者你可以丰富你的BLL类型以返回正确的EF类型,但负面的是SoC,因为你将打破松耦合规则。
public T CreateEntity<T>() where T : class, new()
{
return new T();
}
根据您的最新编辑,我根据您的处理方式优化了您的代码。
public T GetById<T>(Guid id) where T : new()
{
T entityBo = new T();
// this should not be needed, you do not use the results anywhere unless your EF type provides for some defaults at the BLL layer but it would be better to include those defaults in the BLL types constructor instead
// var entity = new EntityFactory().CreateEntity<T>(); // That's the problem line: T is UsersBo, but I need a new Users (EF entity) in order to do the mapping later
// this is the line where you need to implement the translation to get the correct repository type
var repository = new EntityFactory().CreateRepository<T>();
try
{
// get your object from your repository or if it returns null then you will end up just returning the default bll instance which is OK
var entity = repository.GetById(id);
if (entity != null)
{
// map what was returned
Mapper.Map(entity, entityBo);
}
}
catch (Exception ex)
{
// no need for this, it adds nothing
// entityBo = default(T);
// do not do this
// throw ex.GetBaseException();
// this is better, it preserves the stack trace and all exception details
throw;
}
return entityBo;
}
答案 2 :(得分:3)
您可以在业务对象和实体对象之间创建映射器。
[TestClass]
public class EFMappingTest {
[TestMethod]
public void MappperTest() {
EntityFactory.Map<UsersBo, Users>();
EntityFactory.Map<RolesBo, Roles>();
EntityFactory.Map<AppObjectsBo, AppObjects>();
var factory = new EntityFactory();
var entity = factory.CreateEntity<UsersBo>();
Assert.IsNotNull(entity);
Assert.IsInstanceOfType(entity, typeof(Users));
}
class Users { }
class UsersBo { }
class Roles { }
class RolesBo { }
class AppObjects { }
class AppObjectsBo { }
public class EntityFactory {
static IDictionary<Type, Type> mappings = new Dictionary<Type, Type>();
public static void Map<TBusinessObject, TEntity>() where TEntity : class, new() {
Map(typeof(TBusinessObject), typeof(TEntity));
}
public static void Map(Type sourceType, Type targetType) {
mappings[sourceType] = targetType;
}
public object CreateEntity<T>() {
Type entityType = null;
return mappings.TryGetValue(typeof(T), out entityType)
? Activator.CreateInstance(entityType)
: null;
}
public TEntity CreateEntity<TEntity>(object businessObject) where TEntity : class {
if (businessObject == null) throw new ArgumentNullException("businessObject");
Type businessObjectType = businessObject.GetType();
Type entityType = null;
return mappings.TryGetValue(businessObjectType, out entityType)
? (TEntity)Activator.CreateInstance(entityType)
: null;
}
}
}
考虑到您对映射数量的关注。你说你有一个约定。您可以通过枚举业务对象并搜索与您的约定匹配的实体来自动化映射,然后映射它们。
答案 3 :(得分:2)
编辑:添加了另一个解决方案
我有3个建议的解决方案,但只有当你的实体和模型对象都有接口时它才有效:
TInterface Instantiate<TEntity, TInterface>()
where TEntity : class, TInterface
where TInterface : class
{
return (TInterface)Activator.CreateInstance(Type.GetType("Namespace" + typeof(TEntity).FullName.Replace("Bo", "")));
}
用法:IEntity instance = Instantiate<EntityType, IEntity>();
Dictionary<Type, Type> _types = new Dictionary<Type, Type>
{
{ typeof(IEntityA), typeof(ModelA) },
{ typeof(IEntityB), typeof(ModelB) }
};
TInterface Instantiate<TInterface>()
where TInterface : class
{
return (TInterface)Activator.CreateInstance(Type.GetType(_types[typeof(TInterface)].FullName.Replace("1", "2")));
}
用法:IEntity instance = Instantiate<IEntity>();
将是@Igor Damiani,@ Nkosi填写字典并调用Activator.CreateInstance
方法的解决方案之间的混合体。
但是所有这一切,只有你有接口才有效。
实体框架Edmx是一个T4模板。 然后你可以编辑它来生成你的旧类(如果你在与edmx相同的程序集中使用模型的程序集)。
或者您可以使用edmx生成字典。
(无论如何,通过edmx创建实体和模型之间的链接变得非常简单)