Помагане на linqtosql datacontext да използва имплицитно преобразуване между колона varchar в базата данни и таблица и персонализиран тип данни в моето приложение

Създавам таблица на mssql база данни, „Поръчки“, която ще съдържа поле varchar(50), „Стойност“, съдържащо низ, който представлява леко сложен тип данни, „Стойност на поръчката“.

Използвам клас linqtosql datacontext, който автоматично въвежда колоната „Стойност“ като низ.

Дадох на класа "OrderValue" оператори за имплицитно преобразуване към и от низ, така че мога лесно да използвам имплицитно преобразуване с класовете linqtosql като този:

// get an order from the orders table
MyDataContext db = new MyDataContext();
Order order = db.Orders(o => o.id == 1);

// use implicit converstion to turn the string representation of the order
// value into the complex data type.
OrderValue value = order.Value;

// adjust one of the fields in the complex data type
value.Shipping += 10;

// use implicit conversion to store the string representation of the complex
// data type back in the linqtosql order object
order.Value = value;

// save changes
db.SubmitChanges();

Въпреки това, наистина бих искал да мога да кажа на класа linqtosql да въвежда това поле като "OrderValue", а не като "string". Тогава бих могъл да избегна сложния код и да пренапиша горното като:

// get an order from the orders table
MyDataContext db = new MyDataContext();
Order order = db.Orders(o => o.id == 1);

// The Value field is already typed as the "OrderValue" type rather than as string.
// When a string value was read from the database table, it was implicity converted
// to "OrderValue" type.
order.Value.Shipping += 10;

// save changes
db.SubmitChanges();

За да постигна тази желана цел, погледнах дизайнера на контекст на данни и избрах полето „Стойност“ на таблицата „Поръчка“.

След това в свойствата промених "Тип" на "global::MyApplication.OrderValue".

Свойството „Тип данни на сървъра“ беше оставено като „VarChar(50) NOT NULL“

Проектът е изграден без грешки.

При четене от таблицата на базата данни обаче ми беше представено следното съобщение за грешка:

Could not convert from type 'System.String' to type 'MyApplication.OrderValue'.
    at System.Data.Linq.DBConvert.ChangeType(Object value, Type type)
    at Read_Order(ObjectMaterializer`1 )
    at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
    at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
    at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
    at Example.OrdersProvider.GetOrders() 
    at ... etc

От проследяването на стека смятам, че тази грешка се случва, докато четете данните от таблицата. Когато се представя с конвертиране на низ към моя персонализиран тип данни, въпреки че присъстват имплицитните оператори за преобразуване, класът DBConvert се обърква и извежда грешка.

Мога ли да направя нещо, за да не се обърка и да извърша имплицитното преобразуване?


person bboyle1234    schedule 01.05.2010    source източник


Отговори (2)


Интересен проблем... Успях да го направя, като разширих частичния клас на контекста на генерираните данни. Там създадох отделен "въведен" обект, който се задава при зареждане на оригиналните данни и замества оригиналните данни при запазване...

namespace LinqSQLTest
{
    partial class DataClasses1DataContext
    {

    }

    partial class Order
    {
        public OrderValue TypedValue { get; set; }

        partial void OnLoaded()
        {
            TypedValue = this.Value;
        }

        partial void OnValidate(System.Data.Linq.ChangeAction action)
        {
            if (action == System.Data.Linq.ChangeAction.Insert ||
                action == System.Data.Linq.ChangeAction.Update)
            {
                if (TypedValue != null) Value = TypedValue;
            }
        }        
    }    
}

За да го използвате, ще получите достъп до вашата „Стойност“ чрез свойството „TypedValue“ вместо това - не е идеално, но това е единственият начин, който виждам да го направя.

private void button1_Click(object sender, RoutedEventArgs e)
{
    DataClasses1DataContext dc = new DataClasses1DataContext();
    Order order = new Order();
    OrderValue value = new OrderValue();
    value.OrderID=22;
    value.OtherStuff="stuff";
    order.Value= value;
    dc.Orders.InsertOnSubmit(order);
    dc.SubmitChanges();

    var incomingOrder = dc.Orders.Where(x => x.ID == 32).FirstOrDefault();
    Console.WriteLine("ID: {0} \r\n Stuff: {1}", incomingOrder.TypedValue.OrderID, incomingOrder.TypedValue.OtherStuff);

    incomingOrder.TypedValue.OrderID += 10;
    dc.SubmitChanges();

    var incomingOrder2 = dc.Orders.Where(x => x.ID == 32).FirstOrDefault();
    Console.WriteLine("ID: {0} \r\n Stuff: {1}", incomingOrder2.TypedValue.OrderID, incomingOrder2.TypedValue.OtherStuff);

}
person MarcE    schedule 01.05.2010
comment
Здравей Марк, благодаря ти за отговора. Очевидно разбирате идеално проблема и сте създали свое собствено решение. Изящно използване на частичните методи OnLoaded и OnValidate; Бих използвал пълно внедряване на новите методи за получаване и задаване на свойства. Имайки предвид вашето предложение като решение, което може да се използва, ще разгледам изходния код на класа DBConvert и ще видя дали има нещо, което пропускам. Например, какво ще стане, ако мога да накарам моя сложен тип да внедри IConvertible‹› интерфейс или нещо подобно? наздраве! - person bboyle1234; 02.05.2010
comment
Е, след като използвах рефлектор за четене на System.Data.Ling.DBConvert, открих, че той прави малко проверка за някои персонализирани типове обекти Linq, след което изпраща заданието на System.Convert. System.Convert проверява за интерфейса IConvertible. Разбира се, интерфейсът IConvertible не помага при преобразуването към моя персонализиран тип данни, тъй като се занимава изключително с родни типове данни, низ, цяло число, двойно и т.н. Никъде няма проверка за неявни или явни оператори за преобразуване и изглежда, че има също да няма интерфейс IAssignableFrom. О, добре. - person bboyle1234; 02.05.2010
comment
следващата стъпка ще бъде да видя дали мога да заменя преобразуването на типа на класа datacontext, преди да стигне до точката на използване на DBConvert - person bboyle1234; 02.05.2010
comment
Не можах да видя никакви други точки за разширяване, когато погледнах - след като го видях да изчезва в System.Convert, реших, че играта е свършила! Можете да направите работата малко по-безопасна, като маркирате оригиналното поле като частно (в дизайнера на LINQ-DB) - поне това принуждава достъпа през новото псевдо-свойство за всичко, така че имате по-добър шанс да уловите проблеми. - person MarcE; 04.05.2010
comment
добра идея, Марк, дано видях този твой коментар преди две години! - person bboyle1234; 08.04.2013

Имайки подобен проблем и използвайки рефлектор, реших, че мога да осигуря публичен статичен метод, наречен Parse, като взема аргумент от низ. System.Data.Linq.DBConvert.ChangeType(object, Type) ще извика метода Parse, ако съществува в класа.

Така че за вашия пример, добавете към вашия клас OrderValue

public static OrderValue Parse(string source) {
    return new OrderValue().FromSourceString(source); // whatever you need to do to construct from the source
}

Това използва .NET 4.5; Не съм сигурен дали тази поддръжка на метода "Parse" съществува в предишния System.Data.Linq или не.

person Keith D    schedule 24.05.2013