Удаление элемента из визуального дерева обычным способом

Я бы хотел удалить FrameworkElement из визуального дерева. Поскольку FrameworkElement имеет свойство Parent, было бы очевидным решить эту проблему, удалив его оттуда:

FrameworkElement childElement;
if(childElement != null && childElement.Parent != null) // In the visual tree
{
   // This line will, of course not complie:
   // childElement.Parent.RemoveFromChildren(childElement);
}

Проблема в том, что свойство Parent FrameworkElement имеет DependencyObject, который не имеет понятия о дочерних элементах. Итак, единственное, что я вижу в решении этой проблемы, - это преобразование Parent, чтобы увидеть, является ли это Border, Panel и т. Д. (Элементы, которые имеют понятие дочерних элементов), и удалить его оттуда:

FrameworkElement childElement;
if(childElement != null && childElement.Parent != null) // In the visual tree
{
   if(childElement.Parent is Panel)
   {
     (childElement.Parent as Panel).Children.Remove(childElement );
   }
   if(childElement.Parent is Border)
   {
     (childElement.Parent as Border).Child = null;
   }
}

Очевидно, это не очень гибкое решение и совсем не универсальный. У нас очень маленькая модель предметной области, поэтому мы используем чистый подход SqlDataReader для нашего ...

Это сценарий:

  • Мы храним файлы, например, относительно большие документы (10–300 МБ), в больших двоичных объектах в нашей базе данных MSSQL. .
  • У нас очень маленькая модель домена, поэтому мы используем чистый подход SqlDataReader для нашего репозитория вместо ORM, чтобы избежать ненужных зависимостей.
  • Мы хотим использовать объекты в контексте сервера на ASP.NET/ASP Веб-страницы .NET MVC.
  • Мы не хотим временно хранить большие двоичные объекты в байтах [], чтобы избежать чрезмерного использования памяти на сервере

Поэтому я реализовал свой собственный SqlBlobReader. Он наследует Stream и IDisposable, и во время создания экземпляра мы должны предоставить SqlCommand, содержащий запрос, который возвращает одну строку с одним столбцом, который, конечно же, является большим двоичным объектом, который мы хотим передать. Тогда мои объекты домена C # могут иметь свойство типа Stream, которое возвращает реализацию SqlBlobReader. Затем этот поток можно использовать при потоковой передаче в FileContentStream в ASP.net MVC и т. Д.

Он немедленно выполнит ExecuteReader с SequentialAccess, чтобы включить потоковую передачу большого двоичного объекта с сервера MSSQL. Это означает, что мы должны быть осторожны, чтобы избавиться от потока как можно скорее при его использовании, и что мы всегда лениво создаем экземпляр SqlBlobReader, когда это необходимо, например, используя вызов репозитория внутри объектов нашего домена.

Тогда мой вопрос:

  • Является ли это умный способ получения потоков больших двоичных объектов на простых старых объектах домена при использовании SqlDataReader вместо ORM?
  • Я не эксперт ADO.NET, кажется ли реализация разумной?

SqlBlobReader.cs:

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;

namespace Foo
{
   /// <summary>
   /// There must be a SqlConnection that works inside the SqlCommand. Remember to dispose of the object after usage.
   /// </summary>
   public class SqlBlobReader : Stream
   {
      private readonly SqlCommand command;
      private readonly SqlDataReader dataReader;
      private bool disposed = false;
      private long currentPosition = 0;

      /// <summary>
      /// Constructor
      /// </summary>
      /// <param name="command">The supplied <para>sqlCommand</para> must only have one field in select statement, or else the stream won't work. Select just one row, all others will be ignored.</param>
      public SqlBlobReader(SqlCommand command)
      {
         if (command == null)
            throw new ArgumentNullException("command");
         if (command.Connection == null)
            throw new ArgumentException("The internal Connection cannot be null", "command");
         if (command.Connection.State != ConnectionState.Open)
            throw new ArgumentException("The internal Connection must be opened", "command");
         dataReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
         dataReader.Read();
         this.command = command; // only stored for disposal later
      }

      /// <summary>
      /// Not supported
      /// </summary>
      public override long Seek(long offset, SeekOrigin origin)
      {
         throw new NotSupportedException();
      }

      /// <summary>
      /// Not supported
      /// </summary>
      public override void SetLength(long value)
      {
         throw new NotSupportedException();
      }

      public override int Read(byte[] buffer, int index, int count)
      {
         long returned = dataReader.GetBytes(0, currentPosition, buffer, 0, buffer.Length);
         currentPosition += returned;
         return Convert.ToInt32(returned);
      }

      /// <summary>
      /// Not supported
      /// </summary>
      public override void Write(byte[] buffer, int offset, int count)
      {
         throw new NotSupportedException();
      }

      public override bool CanRead
      {
         get { return true; }
      }

      public override bool CanSeek
      {
         get { return false; }
      }

      public override bool CanWrite
      {
         get { return false; }
      }

      public override long Length
      {
         get { throw new NotSupportedException(); }
      }

      public override long Position
      {
         get { throw new NotSupportedException(); }
         set { throw new NotSupportedException(); }
      }

      protected override void Dispose(bool disposing)
      {
         if (!disposed)
         {
            if (disposing)
            {
               if (dataReader != null)
                  dataReader.Dispose();
               SqlConnection conn = null;
               if (command != null)
               {
                  conn = command.Connection;
                  command.Dispose();
               }
               if (conn != null)
                  conn.Dispose();
               disposed = true;
            }
         }
         base.Dispose(disposing);
      }

      public override void Flush()
      {
         throw new NotSupportedException();
      }

   }

}

] В Repository.cs:

  public virtual Stream GetDocumentFileStream(int fileId)
  {
     var conn = new SqlConnection {ConnectionString = configuration.ConnectionString};
     var cmd = new SqlCommand
                  {
                     CommandText =
                        "select DocumentFile " +
                        "from MyTable " +
                        "where Id = @Id",
                     Connection = conn,
                  };


     cmd.Parameters.Add("@Id", SqlDbType.Int).Value = fileId;
     conn.Open();
     return new SqlBlobReader(cmd);
  }

В DocumentFile.cs:

  public Stream GetStream()
  {
     return repository.GetDocumentFileStream(Id);
  }

В DocumentController.cs:

  // A download controller in ASP.net MVC 2

  [OutputCache(CacheProfile = "BigFile")]
  public ActionResult Download(int id)
  {
     var document = repository.GetDocument(id);
     return new FileStreamResult(document.DocumentFile.GetStream(), "application/pdf")
               {
                  FileDownloadName = "Foo.pdf";
               };
  }
9
задан Geir Sørensen 9 December 2010 в 09:59
поделиться