【问题标题】:Get Dependent Entities获取依赖实体
【发布时间】:2019-02-27 15:24:01
【问题描述】:

我有一个使用 EntityFramework 6 和 SQL Server 的数据库应用程序。 在删除实体的情况下。我想向用户展示每个将被删除的依赖实体,所选实体对应于 ON DELETE 约束。在实际删除之前。

所以我的问题是:

是否有任何使用 EntityFramework 或直接使用 SQL Server 特定查询来获取它们的轻量级可能性?

非常感谢。

【问题讨论】:

  • 您对sys.foreign_keys 之类的系统表和编写原始 SQL 查询感觉如何?
  • 不太舒服,但我愿意学习。所以 sys.foreign 键会显示所有外键。现在我需要由这个 FK“连接”的特定实体。正确的?还有其他系统表吗?
  • 是的。你知道我要去哪里。您的表可能是父表或子表。所以你可能需要两个查询。我将在下面发布一些示例 SQL。

标签: c# sql-server entity-framework


【解决方案1】:

此查询应为您提供所需的内容。在我的示例中,我找到了与我的表“客户”相关的所有表。

SELECT ParentTable.Name AS ParentTable, ChildTable.Name AS ChildTable
FROM sys.foreign_keys FK 
    INNER JOIN sys.tables ParentTable ON FK.parent_object_id = ParentTable.object_id
    INNER JOIN sys.tables ChildTable ON FK.referenced_object_id = ChildTable.object_id
WHERE ParentTable.Name = 'Customers'
UNION
SELECT ParentTable.Name AS ParentTable, ChildTable.Name AS ChildTable
FROM sys.foreign_keys FK 
    INNER JOIN sys.tables ParentTable ON FK.parent_object_id = ParentTable.object_id
    INNER JOIN sys.tables ChildTable ON FK.referenced_object_id = ChildTable.object_id
WHERE ChildTable.Name = 'Customers'

如果您不想将所有这些 SQL 粘贴到您的解决方案中,您可以将该查询放入视图中

CREATE VIEW dbo.RelatedTables
AS
SELECT ParentTable.Name AS ParentTable, ChildTable.Name AS ChildTable
FROM sys.foreign_keys FK 
    INNER JOIN sys.tables ParentTable ON FK.parent_object_id = ParentTable.object_id
    INNER JOIN sys.tables ChildTable ON FK.referenced_object_id = ChildTable.object_id

然后像表格一样查询您的 VIEW:

SELECT * FROM dbo.RelatedTables 
WHERE ParentTable = 'Customers' OR ChildTable = 'Customers'

【讨论】:

  • 非常感谢。这无疑是朝着正确的方向发展。例如,我现在需要找到与一个“客户”相关的所有实体。我想也许还有另一个表没有显示表关系,而是显示了直接实体关系。但我认为情况并非如此。所以我将不得不这样做。
【解决方案2】:

我现在已经为我的问题找到了一个可行的解决方案。以防万一其他人感兴趣。我会在这里发布。它基于 Eldho 的评论。您可以使用实体框架获取依赖对象。如前所述,RelationshipManager 已经拥有所有依赖对象。

我为 DbContext 编写了两种扩展方法:一种用于获取依赖于给定实体的所有实体。还有一个是获取给定实体所依赖的所有实体。

public static class DbContextExtensions
{
/// <summary>
/// Gets all entities the given entity is relying on.
/// Will cast the result to a given Type (Entity Base Class / Interface, whatever)
/// </summary>
public static List<TEntity> GetAllDependentEntities<TEntity>(this DbContext ctx, TEntity entity)
  where TEntity : class
{
  return ctx.GetAllRelatedEntities(entity, IsRelationshipParent);
}

/// <summary>
/// Gets all Entities relying on the given entity
/// Will cast the result to a given Type (Entity Base Class / Interface, whatever)
/// </summary>
public static List<TEntity> GetAllEntitiesDependingOn<TEntity>(this DbContext ctx, TEntity entity)
  where TEntity : class
{
  return ctx.GetAllRelatedEntities(entity, IsRelationshipChild);
}

private static List<TEntity> GetAllRelatedEntities<TEntity>(this DbContext ctx, TEntity entity, Func<IRelatedEnd, bool> relationshipFilter)
  where TEntity : class
{
  var result = new List<TEntity>();

  var queue = new Queue<TEntity>();
  queue.Enqueue(entity);

  while (queue.Any())
  {
    var current = queue.Dequeue();

    var foundDependencies = ctx.GetRelatedEntitiesFrom<TEntity>(current, relationshipFilter);
    foreach (var dependency in foundDependencies)
    {
      if (!result.Contains(dependency))
        queue.Enqueue(dependency);
    }

    result.Add(current);
  }

  return result;
}


private static List<TEntity> GetRelatedEntitiesFrom<TEntity>(this DbContext ctx, object entity, Func<IRelatedEnd, bool> relationshipFilter)
  where TEntity : class
{
  var stateManager = (ctx as IObjectContextAdapter)?.ObjectContext?.ObjectStateManager;

  if (stateManager == null)
    return new List<TEntity>();

  if (!stateManager.TryGetRelationshipManager(entity, out var relationshipManager))
    return new List<TEntity>();

  return relationshipManager.GetAllRelatedEnds()
                            .Where(relationshipFilter)
                            .SelectMany(ExtractValues<TEntity>)
                            .Where(x => x != null)
                            .ToList();
}

private static IEnumerable<TEntity> ExtractValues<TEntity>(IRelatedEnd relatedEnd)
  where TEntity : class
{
  if (!relatedEnd.IsLoaded)
    relatedEnd.Load();

  if (relatedEnd is IEnumerable enumerable)
    return ExtractCollection<TEntity>(enumerable);
  else
    return ExtractSingle<TEntity>(relatedEnd);
}

private static IEnumerable<TEntity> ExtractSingle<TEntity>(IRelatedEnd relatedEnd)
  where TEntity : class
{
  var valueProp = relatedEnd.GetType().GetProperty("Value");
  var value = valueProp?.GetValue(relatedEnd);

  yield return value as TEntity;
}

private static IEnumerable<TEntity> ExtractCollection<TEntity>(IEnumerable enumerable)
{
  return enumerable.OfType<TEntity>();
}

private static bool IsRelationshipParent(IRelatedEnd relatedEnd)
  => relatedEnd.SourceRoleName.Contains("Target");

private static bool IsRelationshipChild(IRelatedEnd relatedEnd)
  => relatedEnd.TargetRoleName.Contains("Target");
}

或在这里查看:https://gist.github.com/felixalmesberger/8a9fde392698e366d5cbb75853efb412

【讨论】:

    猜你喜欢
    • 2017-07-15
    • 2014-06-24
    • 2022-01-07
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 2016-09-03
    相关资源
    最近更新 更多