2010-12-13 15 views
12

Có vẻ như tôi không thể tìm thấy bất kỳ câu trả lời nào cho "cách sử dụng phương pháp tiếp cận EAV với các công cụ ORM", vì vậy tôi sẽ thử vận ​​may của mình ở đây.Làm cách nào để lưu trữ siêu dữ liệu mở rộng theo cách thân thiện với ORM trong .NET?

Giả sử tôi có một Entities Bảng:

ID -> int 
Name -> nvarchar(50) 

Một Images Bảng:

EntityID -> int 
Width -> int 
Height -> int 

Và một Songs Bảng:

EntityID -> int 
Duration -> decimal(12,3) 

tôi cần phải thêm siêu dữ liệu mở rộng cho các đối tượng (cặp khóa-giá trị không xác định có thông tin loại), để tôi là ble phát hành các truy vấn như:

Find me tất cả các bài hát có một Duration dài hơn 3 phút, với một Name bắt đầu bằng 'Các', với siêu dữ liệu hoàn thành các tiêu chí:

  • HasGuitarSolo được thiết lập là true
  • GuitarSoloDuration lớn hơn 30 giây

Và sắp xếp các kết quả trên GuitarSoloDuration thứ tự giảm dần.

Tôi không muốn tạo các cột HasGuitarSolo, GuitarSoloDuration, v.v. trong cơ sở dữ liệu, Lý tưởng nhất là tôi lưu trữ chúng trong lược đồ giống như EAV hoặc lược đồ thay thế không yêu cầu kiến ​​thức về khóa lên phía trước.

Trả lời

3

Thêm một cột vào bảng gọi là 'siêu dữ liệu và đưa XML trong đó. SQL server cho phép bạn xem xét một blob đầy XML như thể nó là các cột bổ sung (có giới hạn).

Đối với ORM, nó phụ thuộc vào cách đối tượng của bạn được cấu trúc.

  • Mục siêu dữ liệu có thể tùy chỉnh vô hạn: bạn đặt các cặp tên-giá trị từ XML trong bộ sưu tập. Nếu ORM của bạn không cho phép điều này, hãy đặt nó thẳng vào một thuộc tính chuỗi, trình setter có thể phân tích nó thành một tài liệu XML (hoặc đối tượng nhanh hơn nếu bạn cần tốc độ). Getter sẽ trả về chuỗi. Sau đó, một thuộc tính riêng biệt 'MetaDataItem (ItemName-> string)' mà không phải ORM'd sẽ đọc các giá trị từ danh sách siêu dữ liệu và cập nhật/thêm chúng với setter của nó.
  • Metadeta là thuộc tính được mã hóa cứng - ánh xạ chúng bằng cách sử dụng truy vấn kéo chúng từ XML.
  • Kết hợp hai thuộc tính được mã hóa cứng cho một số mục - có bộ định vị/getters của chúng gọi MetaDataItem.
  • Đảo ngược ngược nếu các thuộc tính nhất định cần được lưu trữ trực tiếp (đặc biệt nếu bạn sắp xếp các danh sách lớn): bạn phải mã hóa các thuộc tính cho siêu dữ liệu đó với các thành viên riêng của chúng, nhưng không ORM các thuộc tính đó. Hardcoded tiết kiệm/tải của những giá trị vào thuộc tính chuỗi đó là ORM'd, và nếu bạn muốn để có thể cập nhật những siêu dữ liệu mã hóa cứng từ các tài sản MetaDataItem là tốt, hardcode chúng nó tại chỗ, quá!

Nếu bạn có toàn bộ chuỗi thuộc tính siêu dữ liệu được mã hóa, ngoài số lượng vô hạn, bạn có thể dễ dàng sử dụng thuộc tính XML và thuộc tính MetaDataItem với danh sách và phản ánh. Nếu tất cả đều được mã hóa cứng, bạn vẫn có thể sử dụng thuộc tính văn bản XML để tải/lưu chúng, ánh xạ một thuộc tính đó chứ không phải các thuộc tính khác.

Sắp xếp chúng bằng truy vấn LINQ trên đối tượng.

Tôi đã làm điều này với thành công lớn và với mỗi mã được mã hóa, công cụ hoạt động tốt hơn và tốt hơn! 2005/.Net 1.1 vì vậy không có ORM, LINQ, chương trình VB.net đầu tiên của tôi vv Nhưng các nhà phát triển khác đã sử dụng truy vấn XML của máy chủ SQL để đọc XML của tôi. Tất nhiên tôi quên mất điều này, thay đổi nó, và vấp phải chúng :-(

Đây là các đoạn mã. Tất cả điều này là: ORM thân thiện = ORM một số thuộc tính, chứ không phải các tài sản khác; Nếu ORM của bạn không cho phép lựa chọn thuộc tính gọi món như vậy, bạn có thể sử dụng thừa kế hoặc bố cục để đánh lừa nó.Xin lỗi, tôi không có thời gian để đăng ví dụ đầy đủ cho mục đích của bạn. Tôi không có mẫu mã ở đây, ở nhà. Tôi sẽ chỉnh sửa và dán mẫu vào ngày mai.

CHỈNH SỬA như đã hứa, đây là đoạn mã:

Public Property ItemType(ByVal stTagName As String) As String 
     Get 
      Dim obj As Object 
      obj = Me.lstMemberList.Item(stTagName) 
      If Not obj Is Nothing Then 
       Return CType(obj, foDataItem).Type 
      End If 
     End Get 
     Set(ByVal Value As String) 
      Dim obj As Object 
      obj = Me.lstMemberList.Item(stTagName) 
      If Not obj Is Nothing Then 
       CType(obj, foDataItem).Type = Value 
      End If 
     End Set 
    End Property 

    Public Function ItemExists(ByVal stTagName As String) As Boolean 
     Return Me.lstMemberList.ContainsKey(stTagName) 
    End Function 

    Public Property ItemValue(ByVal stTagName As String, Optional ByVal Type4NewItem As String = "") As String 
     Get 
      Dim obj As Object 
      obj = Me.lstMemberList.Item(stTagName) 
      If obj Is Nothing Then 
       Dim stInternalKey As String = "" 
       Try 
        stInternalKey = Me.InternalKey.ToString 
       Catch 
       End Try 
       If stTagName <> "InternalKey" Then '' // avoid deadlock if internalkey errs! 
        Throw New ApplicationException("Tag '" & stTagName & _ 
         "' does not exist in FO w/ internal key of " & stInternalKey) 
       End If 
      Else 
       Return CType(obj, foDataItem).Value 
      End If 
     End Get 
     Set(ByVal Value As String) 
      '' // if child variation form... 
      If bLocked4ChildVariation Then 
       '' // protect properties not in the list of allowed updatable items 
       If Not Me.GetChildVariationDifferentFields.Contains(stTagName) Then 
        Exit Property 
       End If 
      End If 
      '' // WARNING - DON'T FORGET TO UPDATE THIS LIST OR YOU WILL NEVER FIND THE BUG! 
      Select Case stTagName 
       Case "PageNum" 
        _PageNum = CInt(Value) 
       Case "Left" 
        _Left = CInt(Value) 
       Case "Top" 
        _Top = CInt(Value) 
       Case "Width" 
        _Width = CInt(Value) 
       Case "Height" 
        _Height = CInt(Value) 
       Case "Type" 
        _Type = String2Type(Value) 
       Case "InternalKey" 
        _InternalKey = CInt(Value) 
       Case "UniqueID" 
        _UniqueID = Value 
      End Select 
      Static MyError As frmErrorMessage 
      Dim obj As Object 
      If Me.lstMemberList.ContainsKey(stTagName) Then 
       Dim foi As foDataItem = CType(Me.lstMemberList.Item(stTagName), foDataItem) 
       If foi.Type = "Number" Then 
        Value = CStr(Val(Value)) 
       End If 
       If foi.Value <> Value Then 
        If bMonitorRefreshChanges Then 
         LogObject.LoggIt("Gonna Send Update for Change " & stTagName & " from " & _ 
          foi.Value & " to " & Value) 
         If Not Me.FormObjectChanged_Address Is Nothing Then 
          FormObjectChanged_Address(Me, stTagName) 
         End If 
        End If 
       End If 
       foi.Value = Value 
      Else 
       Me.lstMemberList.Add(stTagName, New foDataItem(Value, Type4NewItem)) 
       Me.alOrderAdded.Add(stTagName) 
      End If 
     End Set 
    End Property 


    Public Function StringVal(ByVal st As String, Optional ByVal stDefault As String = "") As String 
     Try 
      StringVal = stDefault 
      Return CType(Me.ItemValue(st), String) 
     Catch ex As Exception 
      Dim bThrowError As Boolean = True 
      RaiseEvent ConversionError(ex, "String=" & Me.ItemValue(st), Me, st, bThrowError) 
      If bThrowError Then 
       LogObject.LoggIt("Error setting tag value in fo.StringVal: " & st) 
       Throw New Exception("Rethrown Exception getting value of " & Me.ID & "." & st, ex) 
      End If 
     End Try 
    End Function 
    Public Function IntVal(ByVal st As String, Optional ByVal iDefault As Integer = 0) As Integer 

    ... 

'' // 'native' values - are normal properties instead of XML properties, which 
    '' // actually makes it harder to deal with b/c of extra updates to sync them, BUT, 
    '' // worth it - as they are read much more than written (sorts, wizard builds, 
    '' // screen redraws, etc) I can afford to be slow when writing to them, PLUS 
    '' // retain the benefits of referencing everything else via ItemValue, PLUS 
    '' // these are just the more 'popular' items. 
    Private _Top As Integer 
    Private _Left As Integer 
    Private _Width As Integer 
    Private _Height As Integer 
    Private _PageNum As Integer 
    Private _Type As pfoType 
    Private _InternalKey As Integer 
    Private _UniqueID As String 

    Public Sub SetNativeValuesFromMyXML() 
     _Top = CInt(CType(Me.lstMemberList("Top"), foDataItem).Value) 
     _Left = CInt(CType(Me.lstMemberList("Left"), foDataItem).Value) 
     _Width = CInt(CType(Me.lstMemberList("Width"), foDataItem).Value) 
     _Height = CInt(CType(Me.lstMemberList("Height"), foDataItem).Value) 
     _PageNum = CInt(CType(Me.lstMemberList("PageNum"), foDataItem).Value) 
     _Type = String2Type(CType(Me.lstMemberList("Type"), foDataItem).Value) 
     _InternalKey = CInt(CType(Me.lstMemberList("InternalKey"), foDataItem).Value) 
     _UniqueID = CType(Me.lstMemberList("UniqueID"), foDataItem).Value 
    End Sub 

    Public Property Top() As Integer 
     Get 
      Return _Top '' // CInt(ItemValue("Top")) 
     End Get 
     Set(ByVal Value As Integer) 
      ItemValue("Top") = Value.ToString 
     End Set 
    End Property 

    Public Property Left() As Integer 
     Get 
      Return _Left '' //CInt(ItemValue("Left")) 
     End Get 

    ... 
2

Bạn có thể thêm một vài bảng như:

[EntitiesExtended] 
EntitiesExtendedId int 
EntitiesExtendedDescription varchar(max) 

[Entities_EntitiesExtended] 
Entities_EntitiesExtendedId int 
EntitiesId int 
EntitiesExtendedId int 
EntitiesExtendedValue varchar(max) 

Vì vậy, nếu bài hát id 1 đã có một màn solo guitar của 34 giây và kéo dài trong 3 phút và 23 giây nó có thể được mô hình hóa như:

[Entities_EntitiesExtended] 
EntitiesId = 1 
EntitiesExtendedId = 1 
EntitiesExtendedValue = "34" 

EntitiesId = 1 
EntitiesExtendedId = 2 
EntitiesExtendedValue = "203" 

[EntitiesExtended] 
EntitiesExtendedId = 1 
EntitiesExtendedDescription = "GuitarSoloDuration" 

[EntitiesExtended] 
EntitiesExtendedId = 2 
EntitiesExtendedDescription = "Duration" 

và sau đó truy vấn như:

select * from Entities e 
join Entities_EntitiesExtended eee on e.id = eee.id 
join EntitiesExtended ee on eee.id = ee.id 
where EntitiesExtendedDescription = "GuitarSoloDuration" 
and cast(EntitiesExtendedValue as int) > 30 

select * from Entities e 
join Entities_EntitiesExtended eee on e.id = eee.id 
join EntitiesExtended ee on eee.id = ee.id 
where EntitiesExtendedDescription = "Duration" 
and cast(EntitiesExtendedValue as int) > 180 
+1

Đây là cách nó có thể được thực hiện trong SQL. Tôi đang tìm kiếm đặc biệt cho một cách thân thiện với ORM. Việc truyền tải có vẻ không thân thiện với SQL cũng như với những người lập bản đồ O/R. Cảm ơn bạn đã cố gắng để giúp đỡ mặc dù. –

+0

@Marcin Seredynski: Nơi lưu trữ dữ liệu sau đó? – sv88erik

+0

@ sv88erik: bởi "thực hiện trong SQL", tôi đã viết truy vấn bằng tay, không có bất kỳ động cơ cơ sở dữ liệu cụ thể trong tâm trí (nếu đó là những gì bạn có nghĩa là). Dữ liệu phải được lưu trữ trong cơ sở dữ liệu quan hệ. Việc truy cập dữ liệu không được yêu cầu truyền ở phía máy chủ. Lý tưởng nhất, nó sẽ có thể lưu trữ các loại CLR thích hợp trong các kiểu cột SQL thích hợp. –

2

Bạn có thể lưu trữ dữ liệu trong SQL Server và sử dụng LINQ to SQL ORM.

Cập nhật: Bạn cũng có thể có một cái nhìn tại NH. LLBL, đây là một ORM/Generator, tổ chức của bạn sẽ có rất nhiều mã trước tạo ra, và nó bắt đầu từ cơ sở dữ liệu.

+0

Vì v3 (được phát hành vào năm 2010), LLBLGen Pro cũng cung cấp các mô hình mô hình hóa các tính năng đầu tiên có thể được sử dụng để cập nhật và tạo các mô hình quan hệ. –

3

Tôi đã làm điều này trong quá khứ bằng cách đặt thuộc tính Extra vào đối tượng của tôi, đây là loại Từ điển hoặc loại dữ liệu tương tự. Sau đó bạn có thể tự do điền dữ liệu này và truy vấn bằng LINQ.

+0

IDictionary hoặc IDictionaty là những gì tôi đang hướng tới. Tuy nhiên, câu hỏi đặt ra là tạo một lược đồ thân thiện với ORM. Tôi thực sự sẽ đánh giá cao nếu bạn có thể giải thích thêm một chút? –

Các vấn đề liên quan