【问题标题】:EF 5 Model First Partial Class Custom Constructor How To?EF 5 模型第一个部分类自定义构造函数如何?
【发布时间】:2013-01-23 16:53:50
【问题描述】:

EF 为我生成了一些部分类,每个类都有一个构造函数,但它说不要碰它们(下面的示例),现在如果我创建自己的次要部分类并且我想要一个构造函数来自动设置一些字段我该怎么做,否则会发生冲突?

//------------------------------------------------------------------------------
// <auto-generated>
//    This code was generated from a template.
//
//    Manual changes to this file may cause unexpected behavior in your application.
//    Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Breakdown.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Call
    {
        public Call()
        {
            this.Logs = new HashSet<Log>();
        }

        ...
    }
}

【问题讨论】:

    标签: entity-framework constructor partial-classes ef-model-first


    【解决方案1】:

    部分方法可以帮助您,在 T4 模板中定义一个无主体的部分方法并在构造函数中调用它。

    public <#=code.Escape(entity)#>()
    {
        ...
        OnInit();
    }
    
    partial void OnInit();
    

    然后在你的部分类中定义部分方法并将你想要在构造函数中做的事情放在里面。如果您不想做任何事情,则无需定义分部方法。

    partial class Entity()
    {
        partial void OnInit()
        {
            //constructor stuff
            ...
        }
    }
    

    http://msdn.microsoft.com/en-us/library/vstudio/6b0scde8.aspx

    【讨论】:

    • 模型更新时会覆盖T4模板吗?为什么微软不包括这个?
    • 不,生成的类中的更改将被覆盖,但不会覆盖模板本身。模板的作用是从模型生成类,因此那里的更改是安全的。我猜为什么微软默认不包含这个可能是为了让生成的类尽可能地整洁,因为它们让程序员能够按照他们想要的方式自定义它。
    【解决方案2】:

    这是不可能的。

    部分类本质上是同一类的一部分。

    没有方法可以被定义两次或被覆盖(同样的规则也适用于构造函数)

    但您可以使用下面提到的解决方法:

    //From file SomeClass.cs - generated by the tool
    public partial class SomeClass
     {
        // ...
     }
    
    
    // From file SomeClass.cs - created by me
    public partial class SomeClass
      {
        // My new constructor - construct from SomeOtherType
        // Call the default ctor so important initialization can be done
        public SomeClass(SomeOtherType value) : this()
          {
    
           }
      } 
    

    更多信息请查看Partial Classes, Default Constructors

    希望对你有帮助。

    【讨论】:

    • 是的,我知道我可以创建一个带参数的构造函数,但实际上我没有参数或想要任何参数。
    • @MuhammadA 那么不幸的是你不能。正如我在帖子的顶部提到的那样。
    • 虽然您不能在部分类中重新定义具有相同签名的构造函数,但您可以从自动生成的类中删除构造函数,以便您手动实现它。
    【解决方案3】:

    我最近也想做同样的事情,最后修改了 T4 模板,以便我可以手动实现自己的无参数构造函数。为此,您可以从生成的类中删除构造函数,并将集合等的实例化移动到构造函数之外,这样:

    public Call()
    {
      this.Logs = new HashSet<Log>();
    }
    

    变成这样:

    private ICollection<Log> logs = new HashSet<Log>();
    public virtual ICollection<Log> Logs 
    { 
      get { return this.logs; } 
      set { this.logs = value; } 
    
    }
    

    我认为的缺点是生成的类不那么“干净”。也就是说,您不能只为您的复杂/导航类型拥有自动实现的属性。

    在您的 model.tt 文件中,您可以通过删除以下代码、将其注释掉或在条件中添加一个 false 来阻止构造函数的生成,这样它就永远不会被执行:

    if (propertiesWithDefaultValues.Any() || complexProperties.Any())
    {
    #>
      public <#=code.Escape(complex)#>()
      {
    <#
        foreach (var edmProperty in propertiesWithDefaultValues)
        {
    #>
          this.<#=code.Escape(edmProperty)#> =
             <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
    <#
        }
    
        foreach (var complexProperty in complexProperties)
        {
    #>
          this.<#=code.Escape(complexProperty)#> = new
            <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
    <#
        }
    #>
      }
    

    然后,您需要在下面进行一些修改,为您的复杂类型和导航类型生成属性。添加一个带有对象实例化的私有变量和一个用于访问这些私有变量的属性,例如:

    if (complexProperties.Any())
    {
      foreach(var complexProperty in complexProperties)
      {
        //generate private var + any instantiation
        //generate property for accessing var
      }
    }
    

    根据模型的复杂性,您可能需要修改其他区域。希望这能让你开始。

    【讨论】:

      【解决方案4】:

      如果我很好理解这个问题,你在创建一个新实体时需要这个构造函数,这是一个之前没有持久化的实体。

      我的情况是为所有日期时间设置一个默认值,即将它们初始化为“时间的开始”:1900-01-01。

      在这种情况下,我使用实体工厂

      public static T GetNewEntity<T> () {
          T e;
          try {
              e = Activator.CreateInstance<T>();
          } catch {
              e = default(T);
          }
          SetDefaults(e);
      
          return e;
      }
      

      每次我需要使用一个新实体时

      Entity e = GetNewEntity<Entity>();
      

      使用 SetDefaults 为:

      public static void SetDefaults (object o) {
          Type T = o.GetType();
          foreach ( MemberInfo m in T.GetProperties() ) {
              PropertyInfo P = T.GetProperty(m.Name);
              switch ( Type.GetTypeCode(P.PropertyType) ) {
                  case TypeCode.String :
                      if ( P.GetValue(o, null) == null ) 
                          P.SetValue(o, String.Empty, null); 
                      break;
                  case TypeCode.DateTime :
                      if ( (DateTime)P.GetValue(o, null) == DateTime.MinValue )
                          P.SetValue(o, EntityTools.dtDef, null); 
                      break;
              }
          }
      }
      

      完整代码是here

      可以重写以考虑实体类型等...

      【讨论】:

        【解决方案5】:
        1. 添加基类:

           public class CallBase
           {
                  protected CallBase()
                  {
                      Initialize();
                  }
          
                  protected abstract void Initialize();
           }
          
        2. 在另一个文件中添加部分类实现

           public partial class Call: CallBase
           {
              protected override void Initialize();
              {
             ...
               }
           }
          

        缺点是初始化方法会在所有集合生物之前被调用。

        【讨论】:

          猜你喜欢
          • 2014-07-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-22
          • 1970-01-01
          • 2015-01-26
          • 2012-03-29
          相关资源
          最近更新 更多