Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 动态数据表到实体*没有*预先硬编码。。。?_C#_Entity Framework - Fatal编程技术网

C# 动态数据表到实体*没有*预先硬编码。。。?

C# 动态数据表到实体*没有*预先硬编码。。。?,c#,entity-framework,C#,Entity Framework,问题: Q:为什么我不能/如何动态地使用DataTable中的数据在一个函数中创建LINQ/EF,该函数使用反射(?)根据可用的内容创建实体实例 [Pseudo-Code] public void DataTableToEntities(DataTable dt){ string entityClassName = dt.Name; ArrayList/List<string> colNames = dt.Columns. names, ... ....

问题:

Q:为什么我不能/如何动态地使用DataTable中的数据在一个函数中创建LINQ/EF,该函数使用反射(?)根据可用的内容创建实体实例

[Pseudo-Code]
public void DataTableToEntities(DataTable dt){
    string entityClassName = dt.Name;
    ArrayList/List<string> colNames = dt.Columns. names, ...
    ....
    using (MyContext ctx = new MyContext()){
        while (row dtRow in dtRows){
            // magic follows:
            DeterminedEntity de = new DeterminedEntity();
            // populate de with dtRow
            ctx.[DeterminedEntity].Add(de);
            // magic ends...
        }
        ctx.SaveChanges;
    }
}
我见过很多关于硬编码的Qs&As,但没有一个是关于动态编码的。我正在寻找一种替代方案,以避免为我的所有实体维护硬编码代码

我的方法的背景/验证:

  • 我现在的处境是,我有很多很多实体
  • 我首先使用的是EF6代码,MVC5和ASP.NET,管理员会上传一份电子表格。每个工作簿转换为数据集,每个选项卡(工作表)转换为数据表
  • 所有这些我都做得很好,使用DataTable名称作为实体类的名称
  • 向用户提供了一个模板电子表格,该电子表格已将他们需要填写的列的名称作为顶行。我会解决这个问题后,不得不手动o。[用户应该能够选择一个类,并且应该为他们制作一个工作簿,以便使用适当的列标题动态下载,但这在列表中]
  • 我在这里的问题是从一个已填充的工作表开始的,该工作表已被带到每个类的DataTable中,由其选项卡名称/DataTable名称确定
  • 给定: -我知道类的名称(来自dt.name),并且我已经验证了它是一个有效的实体。 -我知道我将用每个数据行更新的列的名称,就像它们在dt的列名中一样

    问题重述,现结合上下文: 为什么我不能/如何动态地使用DataTable中的数据在一个函数中创建LINQ/EF,该函数使用反射(?)根据可用的内容创建实体实例

    [Pseudo-Code]
    public void DataTableToEntities(DataTable dt){
        string entityClassName = dt.Name;
        ArrayList/List<string> colNames = dt.Columns. names, ...
        ....
        using (MyContext ctx = new MyContext()){
            while (row dtRow in dtRows){
                // magic follows:
                DeterminedEntity de = new DeterminedEntity();
                // populate de with dtRow
                ctx.[DeterminedEntity].Add(de);
                // magic ends...
            }
            ctx.SaveChanges;
        }
    }
    
    格特的解释:

    thisTypeInfo.GetDeclaredProperty获取该类型声明的属性 它本身,而不是它的超类型。使用thisTypeInfo.GetProperty.–格特·阿诺德

    我简直不敢相信我能用一种聪明人可以解决的方式来描述一个我几乎不了解的问题


    ===============================================================================

    一句话中的解决方案:

    该程序获取一个电子表格文件(.xlsx),读取EF DbSet类的选项卡名称,以及属性名称和值的列名和值,并将它们作为单个实体保存在EF安装的数据存储中(可与SQLite和SQL Server 2014一起使用)

    最酷的是,应用程序中不需要硬编码EF类名、属性等。这意味着您可以创建一个Excel模板,让人填写(只需在模板中填写您给他们的列),并且可以导入该文件而无需进一步的麻烦

    你需要为自己创造的一切都在这篇文章中

    我想把它用于我自己正在编写的应用程序,但有点惊讶的是,没有人愿意做同样的事情。有100多个EF类和基类,答案(即使在这里)是蹲下来,咬紧牙关,硬编码一切

    我将在这里发布尽可能完整的工作代码。这段代码对于任何试图让自己的软件更易于使用的人来说都是一个巨大的进步。为了完全隔离问题/Poc/答案,我使用了:


    VS 2015,SQLite latest,SQLite.CodeFirst,System.Data.SQLite[.Linq和.EF6](如果仔细观察,您会发现每一行的列类型(EF属性)类型都会被检查,即使在第一行之后我们知道类型(int,string,…)。我可以在第一行记下并记住这些类型,并在随后的行中引用这些类型。我将在博客上保留升级版本。
    // All details from the property to update/set:
    //PropertyInfo theProp = thisTypeInfo.GetDeclaredProperty(entAttrName);
    PropertyInfo theProp = thisTypeInfo.GetProperty(entAttrName);
    
    public DbSet<Person> Persons { get; set; }
    public DbSet<Child> Children { get; set; }
    
    sChildFoo,iChildBar,dChildBaz,PersonAge,PersonWeight,PersonName
    Norwich,29884,1.2,34,123,Fred Flintstone
    Waterford,34990,3.4,56,210,Barney Rubble
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DynamicEFLoading
    {
        public abstract class Person
        {
            [Key]
            public int PersonId { get; set; }
            public int PersonAge { get; set; }
            public int PersonWeight { get; set; }
            public string PersonName { get; set; }
        }
        class Child : Person
        {
            [Key]
            [Column(Order = 0)]
            public int Id { get; set; }
            public string sChildFoo { get; set; }
            public int iChildBar { get; set; }
            public double dChildBaz { get; set; }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data.SQLite;
    using NLog;
    using System.Reflection;
    using System.Data.Entity;
    using Spire.Xls;
    using System.Data;
    using System.Collections;
    
    namespace DynamicEFLoading
    {
        class TestProgram
        {
            private static Logger logit = LogManager.GetCurrentClassLogger();
            static void Main()
            {
                DateTime dtStart = new DateTime(DateTime.Now.Ticks);
                DateTime dtStop = new DateTime();
                TestProgram s = new TestProgram();
                Utils u = new Utils();
                s.p("Starting at " + DateTime.Now.ToLongTimeString());
                // for this test, I leave this door open the whole time...
                using (MyDbContext ctx = new MyDbContext())
                {
                    //###########################################################################
                    //
                    // create a row in db each time run...
                    //
                    Random rnd = new Random();
                    // 
                    Child c1 = new DynamicEFLoading.Child();
                    // Age, Weight, Name all come from base class Person()
                    c1.PersonAge = rnd.Next(120);
                    c1.PersonWeight = rnd.Next(85, 300);
                    c1.PersonName = String.Format("{0} {1}", Utils.GenerateName(6), Utils.GenerateName(8));
                    s.p(String.Format("Created .Name: {0}", c1.PersonName));
                    //
                    c1.dChildBaz = rnd.NextDouble();
                    c1.iChildBar = rnd.Next(99999);
                    c1.sChildFoo = String.Format("{0}", Utils.GenerateName(10));
                    s.p(String.Format("Created .sParentFoo: {0}", c1.sChildFoo));
                    //
                    ctx.Children.Add(c1);
                    //_______________________________________________________
                    ctx.SaveChanges();
                    //###########################################################################
                    //
                    // in production, there would be no hard coding...
                    //
                    string fileLocation = @"C:\Users\<some user>\Desktop\test.xlsx";
                    //
                    // NOTE! Here I am specifying the only tab(ws name) in the wb. This is easily changed 
                    //      to access all tabs by index, using Worksheet ws = wb.Worksheets[index], and
                    //      a simple loop through them all. In my first test, I even verify the tab had
                    //      a corresponding table in DB before continuing... 
                    string tableName = "Child";
                    //----------------------------------------------------------
                    // freeSpire.xls
                    Workbook wb = new Workbook();
                    wb.LoadFromFile(fileLocation);
                    //----------------------------------------------------------
                    // see NOTE! just above...
                    Worksheet ws = wb.Worksheets[tableName];
                    //----------------------------------------------------------
                    // create a DataTable
                    DataTable dt = new DataTable(tableName);
                    // load it with data from whoile worksheet (ws)
                    dt = ws.ExportDataTable();
                    // from now on, we use DataTable-not spreadsheet
                    //----------------------------------------------------------
                    s.p(String.Format("wb.WorkSheets count: " + wb.Worksheets.Count));
                    s.p(String.Format("ws.Rows count: " + ws.Rows.Count()));
                    s.p(String.Format("dt.Rows.Count: " + dt.Rows.Count));
                    s.p(String.Format("dt.Name: " + dt.TableName));
                    //==========================================================
                    // getting assembly name programmatically fails when a project is inside a solution
                    //  in VS. It assumes ...\ProjName\ProjName\... whis isn't how solutions go... 
                    string pathToAssembly = @"C:\Users\<some user>\Documents\Visual Studio 2015\Projects\DynamicEFLoading\DynamicEFLoading\bin\Debug\DynamicEfLoading.exe";
                    // string pathToAssembly = @".\DynamicEfLoading.exe";
                    // create an 'anonymous', or ghost class:
                    var aClass = u.CreateInstanceOf(dt.TableName, pathToAssembly);
                    // display class type
                    s.p(String.Format("aClass.FullName: " + aClass.GetType().FullName));
                    //==========================================================
                    //
                    // creating a DbSet for the dt's entities. It isn't good enough to just create
                    //  the class itself-it needs to be from the DbContext (ctx). or else you can't
                    //  ctx.SaveChanges();    
                    //
                    s.p(String.Format("Creating 'dbs' object..."));
                    DbSet dbs = ctx.Set(aClass.GetType());
                    // But you can't att attributes/properties to a DbSet-only to the class it is
                    //  derived from, so we then use the DbSet (dbs) for this class to create an
                    //  empty class that we can populate & later add to DB via ctx:
                    var theObj = dbs.Create(aClass.GetType());
                    // make sure it's the right one:
                    s.p(String.Format("GetType: " + theObj.GetType()));
                    //____________________________________________________________________________
                    int i = 0; // used to keep track of each column as we go through the dt
                    foreach (DataRow dr in dt.Rows) // each dr in the dt is a separate instance of the theObj class
                    {
                        s.p(String.Format("================= row =================================="));
                        i = 0; // I don't like to put var instantiation in a loop...
                        // each drItem is the content for the row (theObj)
                        foreach (string drItem in dr.ItemArray)
                        {
                            s.p(String.Format("================= col {0} ", i));
                            string entAttrName = dt.Columns[i].ToString();
                            string entAttrValue = dr[i].ToString();
                            // column (property) name:
                            s.p("[" + i + "] Item: " + entAttrName.ToString());
                            // the value of that property to load into this class' property
                            s.p("[" + i + "] Value: " + entAttrValue.ToString());
                            // which type of data is this property? (string, int32, double...)
                            // -also has data like if nullable, etc. of use in later refinements...
                            TypeInfo thisTypeInfo = theObj.GetType().GetTypeInfo();
                            // All details from the property to update/set:
                            PropertyInfo theProp = thisTypeInfo.GetProperty(entAttrName);
                            //___________________________________________________________________
                            // need to determine the property type, converting entAttrValuefrom string:
                            // good debugging info at this stage to see what we've discovered from the class dynamically at rn time...
                            s.p("theProp.DeclaringType.FullName of attr: " + theProp.DeclaringType.FullName);
                            s.p("theProp.GetSetMethod(true).ToString() of attr: " + theProp.GetSetMethod(true).ToString());
                            s.p("theProp.GetType().ToString() of attr: " + theProp.GetType().ToString());
                            s.p("theProp.Name of attr: " + theProp.Name);
                            s.p("theProp.PropertyType.ToString() of attr: " + theProp.PropertyType.ToString());
                            s.p("theProp.ReflectedType.ToString() of attr: " + theProp.ReflectedType.ToString());
                            s.p("theProp.ReflectedType.ToString() of attr: " + theProp.SetMethod.ReturnType.ToString());
                           /* update entAttrName with entAttrValue:
                            *
                            * At this point, my values in the DataTable are all strings, but the class itself 
                            *   stores that value as who-knows-what. So here I just parse out what kind it is from three
                            *   common types. In future, may need to add more, but for now, these are the big 4: 
                            * 
                            * String, Integer, DatTime, and Double
                            */
                            if (theProp.PropertyType.ToString() == "System.String")
                            {
                                theProp.SetValue(theObj, (String)entAttrValue);
                                logit.Debug("Set {0} value: {1}",
                                    theProp.PropertyType.ToString(),
                                    entAttrValue);
                            }
                            else if (theProp.PropertyType.ToString().Contains("System.Int32"))
                            {
                                theProp.SetValue(theObj, int.Parse(entAttrValue));
                                logit.Debug("Set {0} value: {1}",
                                    theProp.PropertyType.ToString(),
                                    entAttrValue);
                            }
                            else if (theProp.PropertyType.ToString().Contains("System.DateTime"))
                            {
                                IFormatProvider culture = new System.Globalization.CultureInfo("en-US", true);
                                DateTime dTime = DateTime.Parse(entAttrValue, culture, System.Globalization.DateTimeStyles.AssumeLocal);
                                theProp.SetValue(theObj, entAttrValue);
                                logit.Debug("Set {0} value: {1}",
                                    theProp.PropertyType.ToString(),
                                    entAttrValue);
                            }
                            else if (theProp.PropertyType.ToString().Contains("System.Double"))
                            {
                                theProp.SetValue(theObj, double.Parse(entAttrValue));
                                logit.Debug("Set {0} value: {1}",
                                    theProp.PropertyType.ToString(),
                                    entAttrValue);
                            }
                            else
                            {
                                logit.Error("Unexpected property type: {0} given. string value: {1}",
                                    theProp.PropertyType.ToString(),
                                    entAttrValue
                                );
                            }
                            i++; // increment column index...
                        } // end foreach (string drItem in dr.ItemArray...
                        // add class to context...
                        dbs.Add(theObj);
                        // to save one by one (each row):
                        ctx.SaveChanges();
                    } // end of foreach (DataRow dr in dt.Rows...
                      // or... to save as batch (at end of worksheet):
                      // ctx.SaveChanges();
                      //###########################################################################
                    dtStop = new DateTime(DateTime.Now.Ticks);
                    TimeSpan tsDuration = dtStop - dtStart;
                    s.p(String.Format("end... took {0} seconds.", tsDuration.TotalSeconds.ToString()));
                    Console.ReadKey();
                } // end using DbContext...
            }
            /*
             * Convenience; writes to both...
             * 
             */
            //private static void p(string message)
            private void p(string message)
            {
                logit.Info(message);
                Console.WriteLine(message);
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using NLog;
    using System.Collections;
    using System.Data;
    
    namespace DynamicEFLoading
    {
        class Utils
        {
            private static Logger logit = LogManager.GetCurrentClassLogger();
    
            public ArrayList HeadersFromDataTable(DataTable theDataTable)
            {
                ArrayList arHeaders = new ArrayList();
                try
                {
                    arHeaders.AddRange(theDataTable.Columns);
                    logit.Info("loaded {0} column headers...", arHeaders.Count);
                }
                catch (Exception ex)
                {
                    logit.Fatal("exception: " + ex.ToString());
                }
                return arHeaders;
            }
    
            public object CreateInstanceOf(string ClassName, string AssemblyFullPath)
            {
                var assemblyFullPath = Assembly.LoadFrom(@"C:\Users\<some user>\Documents\Visual Studio 2015\Projects\PoliticWebSite\ScratchConsole1\bin\Debug\ScratchConsole1.exe");
                var assembly = Assembly.GetExecutingAssembly();
                //var assembly = Assembly.LoadFrom(assemblyFullPath);
                var type = assembly.GetTypes().First(t => t.Name == ClassName);
                return Activator.CreateInstance(type);
            }
    
            public static string GenerateName(int len)
            {
                Random rndSeed = new Random(DateTime.Now.Millisecond);
                Random r = new Random(rndSeed.Next());
                string[] consonants = { "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "l", "n", "p", "q", "r", "s", "sh", "zh", "t", "v", "w", "x" };
                string[] vowels = { "a", "e", "i", "o", "u", "ae", "y" };
                string Name = "";
                Name += consonants[r.Next(consonants.Length)].ToUpper();
                Name += vowels[r.Next(vowels.Length)];
                int b = 2; //b tells how many times a new letter has been added. It's 2 right now because the first two letters are already in the name.
                while (b < len)
                {
                    //_________________________________________________________________
                    rndSeed = new Random(DateTime.Now.Millisecond);
                    r = new Random(rndSeed.Next());
                    Name += consonants[r.Next(consonants.Length)];
                    b++;
                    //_________________________________________________________________
                    rndSeed = new Random(DateTime.Now.Millisecond);
                    r = new Random(rndSeed.Next());
                    Name += vowels[r.Next(vowels.Length)];
                    b++;
                    //_________________________________________________________________
                }
                return Name;
            }
        }
    }
    
    2016-11-08 22:18:14.2500 INFO Starting at 10:18:14 PM
    2016-11-08 22:18:14.3499 INFO Created .Name: Tytaetae Tytaetaetae
    2016-11-08 22:18:14.3499 INFO Created .sParentFoo: Baebabababa
    2016-11-08 22:18:15.2181 INFO wb.WorkSheets count: 2
    2016-11-08 22:18:15.2181 INFO ws.Rows count: 3
    2016-11-08 22:18:15.2338 INFO dt.Rows.Count: 2
    2016-11-08 22:18:15.2338 INFO dt.Name: Child
    2016-11-08 22:18:15.2487 INFO aClass.FullName: DynamicEFLoading.Child
    2016-11-08 22:18:15.2487 INFO Creating 'dbs' object...
    2016-11-08 22:18:15.2644 INFO GetType: DynamicEFLoading.Child
    2016-11-08 22:18:15.2644 INFO ================= row ==================================
    2016-11-08 22:18:15.2644 INFO ================= col 0 
    2016-11-08 22:18:15.2801 INFO [0] Item: sChildFoo
    2016-11-08 22:18:15.2801 INFO [0] Value: Norwich
    2016-11-08 22:18:15.2801 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Child
    2016-11-08 22:18:15.2958 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_sChildFoo(System.String)
    2016-11-08 22:18:15.2958 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
    2016-11-08 22:18:15.3114 INFO theProp.Name of attr: sChildFoo
    2016-11-08 22:18:15.3114 INFO theProp.PropertyType.ToString() of attr: System.String
    2016-11-08 22:18:15.3271 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
    2016-11-08 22:18:15.3271 INFO theProp.ReflectedType.ToString() of attr: System.Void
    2016-11-08 22:18:15.3271 DEBUG Set System.String value: Norwich
    2016-11-08 22:18:15.3428 INFO ================= col 1 
    ...
    2016-11-08 22:18:16.1237 INFO ================= row ==================================
    2016-11-08 22:18:16.1394 INFO ================= col 0 
    2016-11-08 22:18:16.1394 INFO [0] Item: sChildFoo
    2016-11-08 22:18:16.1551 INFO [0] Value: Waterford
    2016-11-08 22:18:16.1551 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Child
    2016-11-08 22:18:16.1551 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_sChildFoo(System.String)
    2016-11-08 22:18:16.1707 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
    2016-11-08 22:18:16.1863 INFO theProp.Name of attr: sChildFoo
    2016-11-08 22:18:16.1863 INFO theProp.PropertyType.ToString() of attr: System.String
    2016-11-08 22:18:16.1863 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
    2016-11-08 22:18:16.2020 INFO theProp.ReflectedType.ToString() of attr: System.Void
    2016-11-08 22:18:16.2020 DEBUG Set System.String value: Waterford
    2016-11-08 22:18:16.2179 INFO ================= col 1 
    ...
    2016-11-08 22:18:16.5772 INFO ================= col 5 
    2016-11-08 22:18:16.5772 INFO [5] Item: PersonName
    2016-11-08 22:18:16.5772 INFO [5] Value: Barney Rubble
    2016-11-08 22:18:16.5772 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Person
    2016-11-08 22:18:16.5927 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_PersonName(System.String)
    2016-11-08 22:18:16.5927 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
    2016-11-08 22:18:16.5927 INFO theProp.Name of attr: PersonName
    2016-11-08 22:18:16.6084 INFO theProp.PropertyType.ToString() of attr: System.String
    2016-11-08 22:18:16.6084 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
    2016-11-08 22:18:16.6240 INFO theProp.ReflectedType.ToString() of attr: System.Void
    2016-11-08 22:18:16.6240 DEBUG Set System.String value: Barney Rubble
    2016-11-08 22:18:16.6397 INFO end... took 2.391686 seconds.