Search code examples
asp.netvisual-studio-2012visual-studio-2015xamarin.formsreusability

Add Xamarin.Forms projects to legacy Visual Studio solution to promote reusability


I have a Visual Studio 2012 solution with 4 projects all written in VB.Net:

  1. Common class library (CommonClassLibrary.DLL)
  2. Small helper class library
  3. ASP.Net Web Forms application (references to #1 and #2)
  4. Windows console application (references to #1) which runs as scheduled task

The CommonClassLibrary contains business-layer domain classes that encapsulate CRUD operations to an on-premises SQL Server 2008 database. Examples are EventItems.vb, Users.vb, Subscribers.vb. These implement IEnumerable of EventItem.vb, User.vb, etc.

Now I want to embark on developing a mobile app to perform "limited" operations against this application database - and I am trying to think if any of this [common] code can be re-used.

I have never mixed VB.Net and C# projects before in the same solution but SO confirms it can be done to leverage legacy code.

If I upgraded my legacy VB.Net solution to Visual Studio 2015, this could (after production testing) be extended with some C# projects using Xamarin templates. Specifically, I think I could add a new project (Cross-Platform->Blank App (Xamarin.Forms Portable) of type C#). The template for this Portable Class Library - say I call it MyMobile.DLL - automatically creates native UI projects such as MyMobile.Droid, etc.

Then I'd add a reference in MyMobile project to CommonClassLibrary project holding my legacy business logic.

In summary, I am trying to reuse my legacy objects with a technology that is new to me. These objects call SQL stored procedures, all of which works fine and I would like to leverage this with a new mobile native app.

I suspect there are several different approaches but I wonder if this concept sketched here is feasible.

EDIT-add code fragments of CommonClassLibrary project:

This project has several "pairs" of objects with a single instance and the plural of the single instance holding a List of the singleton. Posted here is the Users class which creates a List of one or more User objects.

Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration
Imports System.Collections
Public Class Users
    Implements IEnumerable(Of User)
#Region "properties"
    Public List As New List(Of User)
#End Region
Public Function GetEnumerator() As IEnumerator(Of User) _
                    Implements IEnumerable(Of User).GetEnumerator
    Return List.GetEnumerator()
End Function
Private Function GetEnumerator1() As IEnumerator _
                    Implements IEnumerable.GetEnumerator
    Return List.GetEnumerator()
    End Function
Public Sub New(ByVal subscriberID As Int32)
    Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
    Dim connection As New SqlConnection(sConnDatabase)
    Dim cmd As SqlCommand
Try
    cmd = New SqlCommand("GetUsersBySubscriberID", connection)
    cmd.CommandType = CommandType.StoredProcedure
    cmd.Parameters.AddWithValue("@SubscriberID", subscriberID)
    connection.Open()
    Dim objReader As SqlDataReader = cmd.ExecuteReader()
    Do While objReader.Read()
        Dim u As User = New User(objReader)
        List.Add(u)
    Loop
    objReader.Close()
    connection.Close()
Catch ex As Exception
    Dim peek As String
    peek = ex.Message
    Throw
End Try
End Sub
Public Sub New(ByVal subscriberID As Int32, ByVal v As Enums.TypeUser)
    Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
    Dim connection As New SqlConnection(sConnDatabase)
    Dim cmd As SqlCommand
Try
    cmd = New SqlCommand("GetUsersBySubscriberID", connection)
    cmd.CommandType = CommandType.StoredProcedure
    cmd.Parameters.AddWithValue("@SubscriberID", subscriberID)
    connection.Open()
    Dim objReader As SqlDataReader = cmd.ExecuteReader()
    Do While objReader.Read()
    Dim u As User = New User(objReader)
                If v = Enums.TypeUser.Administrator Then            'If we want admins, then include vendor admins too
                    If u.TypeUserCode = Enums.TypeUser.Administrator Or _
                       u.TypeUserCode = Enums.TypeUser.VendorAdmin Then
                        List.Add(u)
                    End If
                Else
                    If u.TypeUserCode = v Then
                        List.Add(u)
                    End If
                End If
  Loop
    objReader.Close()
    connection.Close()
Catch ex As Exception
    Dim peek As String
    peek = ex.Message
    Throw
End Try
End Sub
End Class

What follows is a selected snippet from the singleton class, User.vb:

Imports System.Data.SqlClient
Imports System.Configuration
Imports TCBCommon.Enums
Public Class User
Public Function IsSuperUser(ByVal v As Enums.TypeUser) As Boolean
        If v = TypeUser.VendorAdmin Then
            Return True
        End If
    Return False
End Function
Public Function IsAdmin(ByVal v As Enums.TypeUser) As Boolean
    If v = TypeUser.Administrator Then
        Return True
    End If
    If v = TypeUser.VendorAdmin Then
        Return True
    End If
    Return False
End Function
Public Sub New(ByVal theObjReader As SqlDataReader)
'   Some caller has already hit the database and passed a SQL datareader to here for creating an instance 
    SetObjectData(theObjReader)
End Sub
Public Sub New(ByVal uID As Int32)
    Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
    Dim connection As New SqlConnection(sConnDatabase)
    Dim cmd As SqlCommand
    Try
        Dim x As String = "SELECT * FROM dbo.UserInfo_v WHERE UserID = " + uID.ToString()
        cmd = New SqlCommand(x, connection)
        cmd.CommandType = CommandType.Text
        connection.Open()
        Dim objReader As SqlDataReader = cmd.ExecuteReader()
        Do While objReader.Read()
            SetObjectData(objReader)
        Loop
        objReader.Close()
        connection.Close()
    Catch ex As Exception
        Throw
    End Try
End Sub
Public Shared Sub Delete(ByVal UserID As Integer)
    Dim con As SqlConnection
    Dim cmd As SqlCommand
    Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
    con = New SqlClient.SqlConnection(sConnDatabase)
    cmd = New SqlClient.SqlCommand("DeleteUserInfo", con)
    cmd.CommandType = CommandType.StoredProcedure
    cmd.Parameters.AddWithValue("@UserID", UserID)
    Try
        con.Open()
        cmd.ExecuteNonQuery()
        cmd.Dispose()
        con.Close()
    Catch ex As Exception
        cmd.Dispose()
        con.Close()
        Dim message As String = String.Format("Error in User.Delete method for userID {0}.", _
                            UserID.ToString)
        Dim myException As New Exception(message, ex)
        Dim exceptionString As String = myException.ToString()
        ' full stack trace
        'TODO: write exceptionString to log.
        Throw myException
    End Try
End Sub
Public Sub Save()
        Dim con As SqlConnection
        Dim cmd As SqlCommand
        Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
        con = New SqlClient.SqlConnection(sConnDatabase)
    Try
        cmd = New SqlClient.SqlCommand("UpdateUserByUserID", con)
        cmd.CommandType = CommandType.StoredProcedure
        cmd.Parameters.AddWithValue("@UserID", UserID)
        cmd.Parameters.AddWithValue("@SubscriberID", SubscriberID)
        cmd.Parameters.AddWithValue("@FirstName", FirstName)
        cmd.Parameters.AddWithValue("@LastName", LastName)
        cmd.Parameters.AddWithValue("@UserName", UserName)
        cmd.Parameters.AddWithValue("@UserEmail", UserEmail)
        cmd.Parameters.AddWithValue("@UserPhone", UserPhone)
        cmd.Parameters.AddWithValue("@CellularNumber", CellularNumber)
        cmd.Parameters.AddWithValue("@CarrierName", CarrierName)
        cmd.Parameters.AddWithValue("@SMSNotifyInd", SMSNotifyInd)
        cmd.Parameters.AddWithValue("@SMSNotifyIndForBackup", SMSNotifyIndForBackup)
        con.Open()
        cmd.ExecuteNonQuery() 'Or, use cmd.ExecuteScalar()
        cmd.Dispose()
        con.Close()
    Catch ex As Exception
        con.Close()
        Throw ex
    End Try
End Sub
Private Sub SetObjectData(ByVal theObjReader As SqlDataReader)
    Try
        Me.UserID = Convert.ToInt32(theObjReader("UserID"))
        Me.SubscriberID = Convert.ToInt32(theObjReader("SubscriberID"))
        Me.TypeUserCode = Convert.ToByte(theObjReader("TypeUserCode"))
        Me.TypeUserDescription = theObjReader("TypeUserDescription")
        Me.UserName = theObjReader("UserName")
        Me.UserPassword = theObjReader("UserPassword")
        Me.UserPhone = If(IsDBNull(theObjReader("UserPhone")), String.Empty, theObjReader("UserPhone"))
        Me.UserEmail = theObjReader("UserEmail")
        Me.FirstName = theObjReader("FirstName")
        Me.LastName = theObjReader("LastName")
        Me.FullName = theObjReader("FullName")
        Me._asIncumbent = theObjReader("PositionsAsIncumbent")
        Me._asBackup = theObjReader("PositionsAsBackup")
        Me._noBackup = theObjReader("PositionsWithoutBackup")
        Me.EventsPersonal = theObjReader("EventsPersonal")
        Me.isFirstLogin = theObjReader("isFirstLogin")
        Me.FLInitialSuffix = theObjReader("FLInitialSuffix")
        Me.CellularNumber = If(IsDBNull(theObjReader("CellularNumber")), String.Empty, theObjReader("CellularNumber"))
        Me.CarrierName = If(IsDBNull(theObjReader("CarrierName")), String.Empty, theObjReader("CarrierName"))
        Me.SMSNotifyInd = If(IsDBNull(theObjReader("SMSNotifyInd")), 0, _
        CInt(theObjReader("SMSNotifyInd")))
' see: https://stackoverflow.com/questions/576431/is-there-a-conditional-ternary-operator-in-vb-net
            Me.SMSNotifyIndForBackup = If(IsDBNull(theObjReader("SMSNotifyIndForBackup")), 0, _
                CInt(theObjReader("SMSNotifyIndForBackup")))
    Catch ex As Exception
        Dim msg As String
        msg = ex.ToString
    End Try
End Sub
#Region "properties"
    Private _UserID As Integer
    Private _SubscriberID As Integer
    Private _TypeUserCode As Integer
    Private _TypeUserDescription As String
    Private _UserName As String
    Private _UserPassword As String
    Private _UserPhone As String
    Private _UserEmail As String
    Private _FirstName As String
    Private _LastName As String
    Private _FullName As String
    Private _asIncumbent As Integer
    Private _asBackup As Integer
    Private _noBackup As Integer
    Private _eventsPersonal As Integer          'number of personal events for the user
    Private _isFirstLogin As Integer
    Private _FLInitialSuffix As Integer
    Private _CellularNumber As String
    Private _CarrierName As String
    Private _SMSNotifyInd As Integer
    Private _SMSNotifyIndForBackup As Integer
    Public Property EventsPersonal() As Integer
        Get
            Return _eventsPersonal
        End Get
        Set(ByVal value As Integer)
            _eventsPersonal = value
        End Set
    End Property
    Public Property PositionsAsIncumbent() As Integer
        Get
            Return _asIncumbent
        End Get
        Set(ByVal value As Integer)
            _asIncumbent = value
        End Set
    End Property
    Public Property PositionsAsBackup() As Integer
        Get
            Return _asBackup
        End Get
        Set(ByVal value As Integer)
            _asBackup = value
        End Set
    End Property
    Public Property PositionsWithoutBackup() As Integer
        Get
            Return _noBackup
        End Get
        Set(ByVal value As Integer)
            _noBackup = value
        End Set
    End Property
    Public Property UserID() As Integer '(int, not null)
        Get
            Return _UserID
        End Get
        Set(ByVal value As Integer)
            _UserID = value
        End Set
    End Property
    Public Property SubscriberID() As Integer '(int, not null)
        Get
            Return _SubscriberID
        End Get
        Set(ByVal value As Integer)
            _SubscriberID = value
        End Set
    End Property
    Public Property TypeUserCode() As Integer '(int, not null)
        Get
            Return _TypeUserCode
        End Get
        Set(ByVal value As Integer)
            _TypeUserCode = value
        End Set
    End Property
    Public Property TypeUserDescription() As String '(nvarchar(100), not null)
        Get
            Return _TypeUserDescription
        End Get
        Set(ByVal value As String)
            _TypeUserDescription = value
        End Set
    End Property
    Public Property UserName() As String '(varchar(80), not null)
        Get
            Return _UserName
        End Get
        Set(ByVal value As String)
            _UserName = value
        End Set
    End Property
    Public Property UserPassword() As String '(varchar(50), not null)
        Get
            Return _UserPassword
        End Get
        Set(ByVal value As String)
            _UserPassword = value
        End Set
    End Property
    Public Property UserPhone() As String '(varchar(20), null)
        Get
            Return _UserPhone
        End Get
        Set(ByVal value As String)
            _UserPhone = value
        End Set
    End Property
    Public Property UserEmail() As String '(varchar(80), null)
        Get
            Return _UserEmail
        End Get
        Set(ByVal value As String)
            _UserEmail = value
        End Set
    End Property
    Public Property FirstName() As String '(varchar(80), null)
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property
    Public Property LastName() As String '(varchar(30), null)
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property
    Public Property FullName() As String
        Get
            Return _FullName
        End Get
        Set(ByVal value As String)
            _FullName = value
        End Set
    End Property

    Public Property isFirstLogin() As Integer '(int, null)
        Get
            Return _isFirstLogin
        End Get
        Set(ByVal value As Integer)
            _isFirstLogin = value
        End Set
    End Property
    Public Property FLInitialSuffix() As Integer '(int, null)
        Get
            Return _FLInitialSuffix
        End Get
        Set(ByVal value As Integer)
            _FLInitialSuffix = value
        End Set
    End Property
    Public Property CellularNumber() As String '(varchar(20), null)
        Get
            Return _CellularNumber
        End Get
        Set(ByVal value As String)
            _CellularNumber = value
        End Set
    End Property
    Public Property CarrierName() As String '(varchar(50), null)
        Get
            Return _CarrierName
        End Get
        Set(ByVal value As String)
            _CarrierName = value
        End Set
    End Property
    Public Property SMSNotifyInd() As Integer '(int, null)
        Get
            Return _SMSNotifyInd
        End Get
        Set(ByVal value As Integer)
            _SMSNotifyInd = value
        End Set
    End Property
    Public Property SMSNotifyIndForBackup() As Integer '(int, null)
        Get
            Return _SMSNotifyIndForBackup
        End Get
        Set(ByVal value As Integer)
            _SMSNotifyIndForBackup = value
        End Set
    End Property
#End Region
End Class

EDIT UPDATE 25 Feb 2016: After a bit more research, I had to rephrase my question in a different post to SO to arrive up with the real answer for me. Reusablity of my data access "layer" will require more code on my web server (i.e. need some sort of web service beyond my CommonClassLibrary.DLL).


Solution

  • Xamarin has a compatibility checker that can assess your code on here . It can analyze and report on the various platforms your interested in supporting, including iOS, Android, Windows Phone and Windows Store.

    I haven't personally used it, but it may be worth a go. As to whether it can analyze VB.Net assemblies is another matter as they don't support it officially.

    Its possible to even use VB.Net in Xamarin.Forms, and there is a guide here, however it indicates several limitations that are quite severe.

    I'll quote them direct from their website:-

    Limitations of Visual Basic in Xamarin.Forms

    As stated on the Portable Visual Basic.NET page, Xamarin does not support the Visual Basic language. This means there are some limitations on where you can use Visual Basic:

    Custom Renderers cannot be written in Visual Basic, they must be written in C# in the native platform projects.

    Dependency Service implementations cannot be written in Visual Basic, they must be written in C# in the native platform projects.

    XAML pages cannot be included in the Visual Basic project - the code-behind generator can only build C#. It is possible to include XAML in a separate, referenced, C# portable class library and use databinding to populate the XAML files via Visual Basic models (an example of this is included in the sample). Xamarin does not support the Visual Basic.NET language.

    To get the most out of Xamarin.Forms you really have to venture into creating Dependency implementations and also Custom Renderers, of which neither of these two are supported in VB.Net.

    As your considering implementing your Portable Library in C# and if you can stick to this then you shouldn't have any issues.

    If you wanted to include your CommonClassLibrary you would have to ensure this is a Portable Class Library. At present I imagine it may be just a normal class library?

    You will have to remove all ASP.Net Web Forms references etc.

    If these are just purely business objects then all the better. If you have any database related code within, you will have to separate also. You may also have to create an Interfaces project possibly and link between the two.

    There would therefore need to be some restructuring possibly.