Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 CLR UDT подмена конструктора  [new]
felix_ff
Member

Откуда: Moscow
Сообщений: 1451
Собственно стенд: Microsoft SQL Server 2005 - 9.00.2198.00 (Intel X86)
Oct 12 2006 19:15:07
Copyright (c) 1988-2005 Microsoft Corporation
Standard Edition on Windows NT 6.1 (Build 7601: Service Pack 1)


+ CLR-UDT

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

namespace BGBS_LogExtension
{
    [Serializable]
    [SqlUserDefinedType(Microsoft.SqlServer.Server.Format.UserDefined, IsByteOrdered=true, MaxByteSize=8000)]
    public class ProcedureLog: INullable, IBinarySerialize
    {
#region fields
        private bool _isNull = true;
        public string ErrStack = "";

        private SqlGuid _LogID;
        private SqlXml _XmlData;

        private SqlGuid current;
        private SqlGuid parent;

        const string nullMarker = "\0\0\0\0\0\0\0\0";

        [XmlElement("Log_LogID")]
        public SqlGuid LogID
        {
            get { return _LogID; }
            set { _LogID = value; }
        }
        [XmlElement("Log_XmlData")]
        public SqlXml XmlData
        {
            get { return _XmlData; }
            set { _XmlData = value; }
        }
#endregion

#region constructor
        public ProcedureLog()
        {
            this.current = SqlGuid.Null;
            this.parent = SqlGuid.Null;
            this.LogID = SqlGuid.Null;
            this.XmlData = SqlXml.Null;
            this._isNull = false;
            this.ErrStack = "Default constructor";
            this.ErrStack += " constructor ended;";
        }
        public ProcedureLog(SqlXml xml_object)
        {
            SqlGuid uid = SqlGuid.Parse(Guid.NewGuid().ToString());
            this.current = uid;
            this.parent = SqlGuid.Null;
            this.LogID = uid;
            this.XmlData = xml_object;
            this._isNull = false;
            this.ErrStack = "Constructor by XML;";
            this.ErrStack += " constructor ended;";
        }
#endregion

#region class system methods
        const char SQUOTE = (char)39;
        const char DQUOTE = (char)34;

        public static SqlXml ConvertString2SqlXml(string xmlData)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            MemoryStream m = new MemoryStream(encoding.GetBytes(xmlData));
            return new SqlXml(m);
        }
        public static SqlXml ConvertXml2SqlXml(XmlDocument xmlData)
        {
            MemoryStream m = new MemoryStream();
            xmlData.Save(m);
            return new SqlXml(m);
        }

        private XmlDocument GetXml(SqlXml x)
        {
            XmlDocument xml = new XmlDocument();
            xml.Load(x.CreateReader());
            return xml;
        }
        private void SaveXml(XmlDocument x)
        {
            XmlData = ConvertXml2SqlXml(x);
        }

        private string QuoteByS(string s)
        { return SQUOTE + s + SQUOTE; }
        private string QuoteByD(string s)
        { return DQUOTE + s + DQUOTE; }
#endregion class system methods

#region Requirements CRL methods and functions
        public static ProcedureLog Null
        {
            get
            {
                ProcedureLog pl = new ProcedureLog();
                pl.ErrStack += " Null method;";
                return pl;
            }
        }

        public bool IsNull
        {
            get { return _isNull; }
        }

        [SqlMethod(DataAccess=DataAccessKind.Read, InvokeIfReceiverIsNull=true)]
        public static ProcedureLog Parse(SqlString s)
        {
             if (s.IsNull)
                 return Null;

            ProcedureLog pl = new ProcedureLog();
            string[] xy = s.Value.Split(",".ToCharArray());
            pl.LogID = SqlGuid.Parse(xy[0]);
            pl.XmlData = ConvertString2SqlXml(xy[1]);
            pl._isNull = false;
            pl.ErrStack += " Parse method ended;";

            return pl;
        }
        public override string ToString()
        {
            if (this.IsNull)
                return "This object is NULL";
            else
            {
                if (String.IsNullOrEmpty(this.ErrStack))
                    return "ErrStack is Null or Empty";
                else
                    return "ToString:" + this.ErrStack;
            }
        }

         void IBinarySerialize.Read(BinaryReader r)
        {
            char[] value = new char[8000];
            for (int i = 0; i < r.BaseStream.Length; i++)
            {
                value[i] = r.ReadChar();
            }

            XmlData = ConvertString2SqlXml(new string(value));
        }
         void IBinarySerialize.Write(BinaryWriter w)
        { 
            if (this.IsNull)
            {
              w.Write(nullMarker);
              return;
            }

            byte[] buffer = Encoding.UTF8.GetBytes(XmlData.Value.ToCharArray());
            w.Write(buffer);
        }
#endregion

#region class logic methods
         [SqlMethod(DataAccess = DataAccessKind.Read, InvokeIfReceiverIsNull = false)]
         public static ProcedureLog NewFromXml(SqlXml xml_object)
         {
             if (xml_object.IsNull)
                 return Null;

             return new ProcedureLog(xml_object);
         }
#endregion
    }

}



declare @x PLog, @xml xml
set @xml = '<row0></row0>'
set @x = PLog::NewFromXml(@xml)
select @x.ToString(), convert(varchar(8000), convert(varbinary(8000), @x))
set @xml = '<row1></row1>'
set @x = PLog::NewFromXml(@xml)
select @x.ToString(), convert(varchar(8000), convert(varbinary(8000), @x))


Вывод
ToString:Default constructor constructor ended; <row0 />
ToString:Default constructor constructor ended; <row1 />



меня смущает фигня что он постоянно пишет "Default constructor" хотя по идее я запускаю метод который должен выполнить перегруженный конструктор с входящим xml?

эта особенность на 2005 или я чего то не догоняю?

http://sqlblog.com/blogs/peter_debetta/archive/2006/06/26/Overloaded-Methods-and-Constructors-in-UDTs.aspx
22 окт 15, 13:55    [18311808]     Ответить | Цитировать Сообщить модератору
 Re: CLR UDT подмена конструктора  [new]
Ex_Soft
Member

Откуда:
Сообщений: 7710
http://sqlblog.com/blogs/peter_debetta/archive/2006/06/26/Overloaded-Methods-and-Constructors-in-UDTs.aspx
Fact #2: You can create overloaded constructors in your UDTs, but keep in mind that...

Constructors with parameters are not directly usable by T-SQL
For a reference type (class), T-SQL calls only the parameterless constructor
For a value type (struct), T-SQL does not call any constructors

На характер можете вместо
+

#region constructor
        public ProcedureLog()
        {
            this.current = SqlGuid.Null;
            this.parent = SqlGuid.Null;
            this.LogID = SqlGuid.Null;
            this.XmlData = SqlXml.Null;
            this._isNull = false;
            this.ErrStack = "Default constructor";
            this.ErrStack += " constructor ended;";
        }
        public ProcedureLog(SqlXml xml_object)
        {
            SqlGuid uid = SqlGuid.Parse(Guid.NewGuid().ToString());
            this.current = uid;
            this.parent = SqlGuid.Null;
            this.LogID = uid;
            this.XmlData = xml_object;
            this._isNull = false;
            this.ErrStack = "Constructor by XML;";
            this.ErrStack += " constructor ended;";
        }
#endregion


сделать а-ля
+

#region constructor
        public ProcedureLog(SqlXml xml_object = SqlXml.Null)
        {
            SqlGuid uid = xml_object != SqlXml.Null ? SqlGuid.Parse(Guid.NewGuid().ToString()) : SqlGuid.Null;
            this.current = uid;
            this.parent = SqlGuid.Null;
            this.LogID = uid;
            this.XmlData = xml_object;
            this._isNull = false;
            this.ErrStack = "Constructor by XML;";
            this.ErrStack += " constructor ended;";
        }
#endregion


по идее должно ругнуться, что отсутствует parameterless constructor...
22 окт 15, 16:21    [18312924]     Ответить | Цитировать Сообщить модератору
 Re: CLR UDT подмена конструктора  [new]
felix_ff
Member

Откуда: Moscow
Сообщений: 1451
Ex_Soft,

так к сожалению не катит, если создавать класс без конструктора принимающего 0 аргументов компилятор ругается:

Ошибка 1 BGBS_LogExtension.ProcedureLog не содержит конструктор, который принимает 0 аргументов C:\Users\user\Desktop\VB_Projects\BGBS_LogExtension\BGBS_LogExtension\Class1.cs 107 35 BGBS_LogExtension


Если попытаться оставить класс с дефолтным конструктором, а конструктор с параметром видоизменить как Вы посоветовали ошибка выглядит так:
Ошибка 1 Значение параметра по умолчанию для "xml_object" должно быть константой времени компиляции C:\Users\user\Desktop\VB_Projects\BGBS_LogExtension\BGBS_LogExtension\Class1.cs 55 49 BGBS_LogExtension

меня что больше все смущает, он ведь в итоге нужный конструктор использует:
public ProcedureLog(SqlXml xml_object)
        {
            SqlGuid uid = SqlGuid.Parse(Guid.NewGuid().ToString());
            this.current = uid;
            this.parent = SqlGuid.Null;
            this.LogID = uid;
                                                                               this.XmlData = xml_object;
            this._isNull = false;
                                                                               this.ErrStack = "Constructor by XML;";
                                                                               this.ErrStack += " constructor ended;";
        }


объект видоизменяется при декларации методом NewFromXml
<row0>
<row1>

но почему тогда метод ToString() передает строку ErrStack от дефолтного конструктора.
Где то лыжи не едут или я слеп как крот :(
получить бы стек вызовов, что бы понимать в какой последовательности sql server создает экземпляры объектов.
22 окт 15, 17:28    [18313321]     Ответить | Цитировать Сообщить модератору
 Re: CLR UDT подмена конструктора  [new]
felix_ff
Member

Откуда: Moscow
Сообщений: 1451
Ex_Soft,

а ну собсна да, вы и написали что должен ругнуться без дефолтного конструктора

в этом плане все предсказуемо.

а вот уже при создании экземпляра начинается магия
22 окт 15, 17:39    [18313381]     Ответить | Цитировать Сообщить модератору
 Re: CLR UDT подмена конструктора  [new]
invm
Member

Откуда: Москва
Сообщений: 9406
felix_ff,

Почему-бы под отладчиком все это дело не посмотреть?
22 окт 15, 18:12    [18313546]     Ответить | Цитировать Сообщить модератору
 Re: CLR UDT подмена конструктора  [new]
Ex_Soft
Member

Откуда:
Сообщений: 7710
felix_ff
...меня что больше все смущает, он ведь в итоге нужный конструктор использует...
...объект видоизменяется при декларации методом NewFromXml...

Чёй-то я никакого видоизменения не наблюдаю:
         [SqlMethod(DataAccess = DataAccessKind.Read, InvokeIfReceiverIsNull = false)]
         public static ProcedureLog NewFromXml(SqlXml xml_object)
         {
             if (xml_object.IsNull)
                 return Null;

             return new ProcedureLog(xml_object);
         }

Вам пришел xml_object, родили ProcedureLog посредством конструктора ProcedureLog(SqlXml xml_object) в котором просто this.XmlData = xml_object. А далее, я так догадываюсь пошла сериализация (ведь [Serializable] же (BTW, потому и требует parameterless constructor (в том числе))): вызывается ProcedureLog() (вот Вам и Ваш this.ErrStack = "Default constructor") с последующим присвоением this.XmlData = xml_object. Теоретически - как-то так должно проистекать... Ну и
invm
Почему-бы под отладчиком все это дело не посмотреть?

дело грит - при'attach'аваетесь к sqlservr.exe (только VS запустите из-под одмина), ставите breakpoint'ы внутри обоих конструкторов и по приходу смотрите стек.
22 окт 15, 18:55    [18313737]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить