【问题标题】:How to get DbSet dynamically in EF Core 3?如何在 EF Core 3 中动态获取 DbSet?
【发布时间】:2020-12-19 15:57:34
【问题描述】:

您好,我正在尝试这样做:

我有更多类,用户可以在其中存储文件 - 这些文件的路径以 JSON 格式存储在 DB 中,文件存储在名为类的文件夹中(因此,当类名为 Foo 时,文件将存储在 Files/ Foo/Foobar.pdf)。

我有一个控制器,用于发送应该删除的文件的路径。在这里我无法解决问题,我需要从数据库中删除这个文件路径,但我不知道如何动态获取 DbSet。

我在这里搜索了很多问题,但几乎每次我都找到了DbContext.Set(),但是ASP.NET CORE 3.0中不存在此方法。你能帮我怎么做吗?

这是我的控制器:

[HttpGet]
        public JsonResult DeleteFile(string id)
        {
            KeyValuePair<string, string> message;
            if (!string.IsNullOrEmpty(id))
            {
                string filePath = Uri.UnescapeDataString(id);
                if (filePath.CheckIfExists())
                {
                    string nameOfInstance = filePath.GetNumberAccordingToFileName();
                    string tableName = filePath[1..(filePath.IndexOf('/', 1) - 1)];
                    Type type = Assembly.GetExecutingAssembly()
                            .GetTypes()
                            .FirstOrDefault(t => t.Name.Equals(tableName,StringComparison.OrdinalIgnoreCase));
                    if(type.IsClass)
                    {
                        var entity = db.GetDbSet(Activator.CreateInstance(type)); //this is not working
                        var item = entity.GetDataFromDbase(nameOfInstance,1,0,"Number","ASC","Number",false,out int filteredResultsCount, out int totalResultsCount, false).FirstOrDefault(); //find the item
                        item = item.DeleteFileFromItem(User.Identity.Name, filePath);
                        db.Update(item);
                        var result = db.SaveChanges(); 
                        if (result > 0)
                        {
                            if (filePath.DeleteFile())
                            {
                                message = new KeyValuePair<string, string>(MessagesHandler.Success, $"File {filePath.Substring(filePath.LastIndexOf("/"))} was successfully deleted.");
                                return Json(new
                                {
                                    status = true,
                                    message
                                });
                            }
                            else
                            {
                                message = new KeyValuePair<string, string>(MessagesHandler.Error, $"File {filePath.Substring(filePath.LastIndexOf("/"))} was not deleted.");
                                return Json(new
                                {
                                    status = false,
                                    message
                                });
                            }
                        } //if the changes were not made in DB
                        message = new KeyValuePair<string, string>(MessagesHandler.Error, $"File {filePath.Substring(filePath.LastIndexOf("/"))} was deleted, error in DB");
                        return Json(new
                        {
                            status = false,
                            message
                        });
                    }

                    
                }//error message when file not found
                message = new KeyValuePair<string, string>(MessagesHandler.Error, $"File {filePath} not found.");
                return Json(new
                {
                    status = false,
                    message
                });
            }//error message when filename is empty
            message = new KeyValuePair<string, string>(MessagesHandler.Error, $"Field ID cannot be empty");
            return Json(new
            {
                status = false,
                message
            });
        }

这是GetDbSet方法:

public static Microsoft.EntityFrameworkCore.DbSet<TEntity> GetDbSet<TEntity>(this DataContext db, TEntity t) where TEntity : class
        {
            Type type = t.GetType();
        return (Microsoft.EntityFrameworkCore.DbSet<TEntity>)typeof(DataContext).GetMethod(nameof(DataContext.Set)).MakeGenericMethod(type).Invoke(db, null);
        }

这里我得到了这个异常:

System.InvalidCastException H结果=0x80004002 Message=无法转换类型为“Microsoft.EntityFrameworkCore.Internal.InternalDbSet1[SmartLab_System.Models.TestRequirement]' to type 'Microsoft.EntityFrameworkCore.DbSet1[System.Object]”的对象。

方法GetDataFromDbase:

public static List<T> GetDataFromDbase<T>(this IEnumerable<T> entity, string searchBy, int take, int skip, string sortBy,
            string sortDir, string columnName, bool showDeactivatedItems, out int filteredResultsCount, out int totalResultsCount, bool countResult = true) where T : class
        {
            IEnumerable<T> helper;
            if (!String.IsNullOrEmpty(searchBy))//if any string to search was given
            {
                //find the properties we would like to search in
                var properties = typeof(T).GetProperties()
                                    .Where(x => x.CanRead && columnName.Contains(x.Name, StringComparison.InvariantCultureIgnoreCase) && columnName.Length == x.Name.Length)
                                    .Select(x => x.GetMethod)
                                    .Where(x => !x.IsStatic);
                                    //.ToList();

                //list of all items where searched value was found
                helper = entity.GetActiveItems(showDeactivatedItems)
                            .Where(m => properties
                                .Select(p => p.Invoke(m, null)?.ToString() ?? string.Empty)
                                .Any(a => a.ToString().Contains(searchBy, StringComparison.InvariantCultureIgnoreCase)) == true);
                            //.ToList();
            }
            else//if no string to search was given
            {
                helper = entity.GetActiveItems(showDeactivatedItems); //all items
            }

            List<T> result;
            if (take < 1)
            {
                result = helper
                            .OrderBy(sortBy + " " + sortDir)
                            .ToList();
            }
            else
            {
                result = helper
                           .OrderBy(sortBy + " " + sortDir)
                           .Skip(skip)
                           .Take(take)
                           .ToList();
            }

            if (countResult)
            {
                filteredResultsCount = helper.Count();//how many items is sent
                totalResultsCount = entity.Count(); //how many items is in database
            }
            else
            {
                filteredResultsCount = 0;//how many items is sent
                totalResultsCount = 0;
            }

        return result;

    }

除了打电话db.GetDbSet(type)我也试过db.GetDbSet(Activator.CreateInstance(type))但没有用。

【问题讨论】:

  • 什么是GetDataFromDbase()?它的目的是什么?
  • 您可能需要使用反射。这应该可以解决问题 => typeof(DbContext).GetMethod(nameof(DbContext.Set).MakeGenericMethod(type).Invoke(db, null) 然后您需要将其转换为正确的类型。
  • 感谢您的回答,我现在已经编辑了关于我使用您的代码得到的结果的问题
  • @joakimriedel 我试过这个,但问题是一样的 - 当输入参数它 IQueryable (在这种情况下 T 应该是我的类)然后当我尝试制作 typeof(T) 我'会得到“对象”然后var properties = typeof(T).GetProperties() .Where(x =&gt; x.CanRead &amp;&amp; columnName.Contains(x.Name, StringComparison.InvariantCultureIgnoreCase) &amp;&amp; columnName.Length == x.Name.Length).Select(x =&gt; x.GetMethod).Where(x =&gt; !x.IsStatic);不起作用

标签: c# entity-framework-core-3.0


【解决方案1】:

我在 cmets (Dynamically access table in EF Core 2.0) 中发布的链接将为您检索集合,但您无法对其进行操作,因为它的类型是 IQueryable 而不是 IQueryable&lt;T&gt;

我认为可以通过使用简单的开关和实体上的通用界面来为您提供更好的解决方案。只要您的数据库中没有 100 多个表,这将很容易维护。

                var entityType = "Drawer";
                var pathToRemove = "somepath";
                var id = 1;

                // get queryable based on entity type
                IQueryable<IHasFiles> queryable = entityType switch
                {
                    "Drawer" => context.Set<Drawer>().OfType<IHasFiles>(),
                    "Bookshelf" => context.Set<Bookshelf>().OfType<IHasFiles>(),
                    _ => throw new ArgumentException("Unknown entity type", nameof(entityType))
                };

                // pick the item and include all files
                var item = await queryable.Where(i => i.Id == id).Include(i => i.Files).SingleAsync();

                // pick the correct file
                var file = item.Files.Single(f => f.Path == pathToRemove);

                // remove it
                item.Files.Remove(file);

                // save changes to db
                await context.SaveChangesAsync();

实体定义如下

        public interface IHasFiles
        {
            int Id { get; }
            ICollection<File> Files { get; set; }
        }

        public class File
        {
            public int Id { get; set; }
            public string Path { get; set; }
        }

        public class Drawer : IHasFiles
        {
            public int Id { get; set; }

            public ICollection<File> Files { get; set; }
        }

        public class Bookshelf : IHasFiles
        {
            public int Id { get; set; }

            public ICollection<File> Files { get; set; }
        }

【讨论】:

  • 感谢提示 - 我已经更改了所有必要的类来实现接口 IHasFiles,然后可以将 IEnumerable 转换为 IEnumerable,然后使用该项目:)。
  • ?我很高兴听到您找到了解决方案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
  • 2021-04-07
  • 2017-12-10
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
相关资源
最近更新 更多