How to setup a remote DataPortal using a console/ windows service? #1807
-
How to setup a remote DataPortal using a console/ windows service?What I am trying to doI am in process of creating a Xamarin Form application, reusing business objects written for a WinForms application. The Business Objects library is created using CSLA ver My task is creating a Remote DataPortal. I read the book I am struggling with the setup by now and could not make the remote DataPortal works so far :(.
Full source code: https://github.com/EricNgo1972/ConsoleApp3tier Below are my steps. Step 1 - Create Business Objects Library (BOL assembly)This is already exists in my Winform App. In this testing library, I just include one simple class : The Contact Record. Editable using Csla;
using Csla.Configuration;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SPC
{
[Serializable]
public class Contact : Csla.BusinessBase<Contact>
{
#region Properties
public static readonly PropertyInfo<string> ContactCodeProperty = RegisterProperty<string>(c => c.ContactCode);
public string ContactCode
{
get { return GetProperty(ContactCodeProperty); }
set { SetProperty(ContactCodeProperty, value); }
}
public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
public string Name
{
get { return GetProperty(NameProperty); }
set { SetProperty(NameProperty, value); }
}
public static readonly PropertyInfo<string> ContactTypeProperty = RegisterProperty<string>(c => c.ContactType);
public string ContactType
{
get { return GetProperty(ContactTypeProperty); }
set { SetProperty(ContactTypeProperty, value); }
}
#endregion
#region Factory
public static void NewContact(string pContactCode)
{
DataPortal.Create<Contact>(new Criteria() { ContactCode = pContactCode });
}
public static void GetContact(string pContactCode)
{
DataPortal.Fetch<Contact>(new Criteria() { ContactCode = pContactCode });
}
public static async Task<Contact> NewContactAsync()
{
return await DataPortal.CreateAsync<Contact>();
}
public static async Task<Contact> GetContactAsync(string pContactCode)
{
return await DataPortal.FetchAsync<Contact>(new Criteria() { ContactCode = pContactCode });
}
public static void DeleteContact(string pContactCode)
{
DataPortal.Delete<Contact>(new Criteria() { ContactCode = pContactCode });
}
#endregion
[Serializable()]
private class Criteria : CriteriaBase<Criteria>
{
public static readonly PropertyInfo<string> ContactCodeProperty = RegisterProperty<string>(c => c.ContactCode);
public string ContactCode
{
get { return ReadProperty(ContactCodeProperty); }
set { LoadProperty(ContactCodeProperty, value); }
}
}
#region Data Access
private void DataPortal_Fetch(Criteria criteria)
{
// TODO: load values into object
var msg = $"Fetching contact: {criteria.ContactCode}";
}
protected override void DataPortal_Insert()
{
// TODO: insert object's data
}
protected override void DataPortal_Update()
{
// TODO: update object's data
}
protected override void DataPortal_DeleteSelf()
{
DataPortal_Delete(new Criteria() { ContactCode = ContactCode });
}
private void DataPortal_Delete(Criteria criteria)
{
// TODO: delete object's data
}
#endregion
}
} ReadOnly class using Csla;
using System;
namespace SPC
{
[Serializable]
public class ContactInfo: Csla.ReadOnlyBase<ContactInfo>
{
public static readonly PropertyInfo<string> ContactCodeProperty = RegisterProperty<string>(c => c.ContactCode);
public string ContactCode
{
get { return GetProperty(ContactCodeProperty); }
private set { LoadProperty(ContactCodeProperty, value); }
}
public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
public string Name
{
get { return GetProperty(NameProperty); }
private set { LoadProperty(NameProperty, value); }
}
public static readonly PropertyInfo<string> ContactTypeProperty = RegisterProperty<string>(c => c.ContactType);
public string ContactType
{
get { return GetProperty(ContactTypeProperty); }
private set { LoadProperty(ContactTypeProperty, value); }
}
internal static ContactInfo GeneratedSampleContactInfo(int Index)
{
return new ContactInfo() { ContactCode = $"G{string.Format("{0:000}",Index)}", Name = $"Contact with auto generated name {rand.Next()}" , ContactType ="TEST" };
}
}
} using Csla;
using System;
using System.Collections.Generic;
using System.Text;
namespace SPC
{
[Serializable]
public class ContactInfoList : Csla.ReadOnlyListBase<ContactInfoList, ContactInfo>
{
public static ContactInfoList GetInfoList(Dictionary<string, string> filters)
{
return DataPortal.Fetch<ContactInfoList>(new Criteria() { Filters = filters});
}
[Serializable()]
private class Criteria : CriteriaBase<Criteria>
{
public static readonly PropertyInfo<Dictionary<string,string>> FiltersProperty = RegisterProperty<Dictionary<string,string>>(c => c.Filters);
public Dictionary<string,string> Filters
{
get { return ReadProperty(FiltersProperty); }
set { LoadProperty(FiltersProperty, value); }
}
}
#region Data Access
private void DataPortal_Fetch(Criteria criteria)
{
// TODO: load values into object
IsReadOnly = false;
for (int i = 0; i < 30; i++)
{
Add(ContactInfo.GeneratedSampleContactInfo(i));
}
IsReadOnly = true;
}
#endregion
}
} Step 2 - Create the Client Console Appusing SPC.UI;
using System;
namespace SPC.UI
{
class Program
{
static void Main(string[] args)
{
ServiceConfig.Init();
Console.WriteLine("Console 3 tier test.");
Console.WriteLine("----------------------");
try
{
var list = SPC.ContactInfoList.GetInfoList(null);
foreach (var item in list)
{
Console.WriteLine($" - {item.ContactCode}. {item.Name}. {item.ContactType}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
} I set the proxy by code as below: using System;
namespace SPC.UI
{
static class ServiceConfig
{
const string AppHostUrl = "http://localhost:8001/";
public static void Init()
{
Console.WriteLine($"Set Dataportal Proxy : {AppHostUrl}");
Csla.ApplicationContext.DataPortalProxy = typeof(Csla.DataPortalClient.HttpProxy).AssemblyQualifiedName;
Csla.ApplicationContext.DataPortalUrlString = AppHostUrl;
}
}
} Step 3 - Create the WindowsHost appI put the hosting logic in a seperate dll. So I can debug it with a console app. In production it will be called by a Windows service. using Csla.Configuration;
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace WindowsSrvHost
{
public class DataPortalHost
{
private ServiceHost _netService;
private string _url = "";
public string GetUrl() => _url;
public void Start()
{
if (_netService !=null)
{
_netService.Close();
}
_url = ConfigurationManager.AppSettings["NetDataPortalUri"];
_netService = new ServiceHost(typeof(Csla.Server.Hosts.WcfPortal), new Uri(_url));
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
_netService.Description.Behaviors.Add(smb);
_netService.Open();
}
public void Stop()
{
if (_netService !=null)
{
_netService.Close();
_netService = null;
}
}
}
} Then I create another Console Project to call this hosting dll: using System;
namespace ConsoleHostTest
{
class Program
{
static void Main(string[] args)
{
try
{
var service = new WindowsSrvHost.DataPortalHost();
service.Start();
Console.WriteLine($"Service started. {service.GetUrl()}");
Console.WriteLine("Enter 'STOP' to stop the service");
while (Console.ReadLine() == "STOP")
{
service.Stop();
Console.WriteLine("Service Stopped.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadKey();
}
}
}
} Result:Run the ConsoleHostTest as Administrator, i can see that the host is working: However, the client somehow could not use the DataPortal service: I expect that the result must be the same as run with Local DataPortal: Thanks for your time. Progress update 1I have kicked out the csla nuget and add the csla source to the solution. Debugging showed me that the Csla.DataPortalClient.HttpProxy supports only Async methods. I changed mycode in BOL public static ContactInfoList GetInfoListAsync(Dictionary<string, string> filters)
{
return DataPortal.Fetch<ContactInfoList>(new Criteria() { Filters = filters});
} to : public async static Task<ContactInfoList> GetInfoListAsync(Dictionary<string, string> filters)
{
return await DataPortal.FetchAsync<ContactInfoList>(new Criteria() { Filters = filters});
} It seems the Client did reach the Wcf service. BUT there is a message format mismatch between the client and server. The client must use .net Standard (Xamarin), but the server provides the .net WCF service. WCF is not supported anymore in .net Core. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
I recommend using the http channel, as WCF is not supported at all on modern .NET platforms. |
Beta Was this translation helpful? Give feedback.
-
Yes, in Csla 4 that is correct. You'll need to change your factory methods on your BOs to use CreateAsync/FetchAsync and return a |
Beta Was this translation helpful? Give feedback.
-
Hi, I managed to setup a http channel as Rocky suggested. It works fine now. The problem is simple to understand : my Client uses http channel (.net core) while the server use the old WCF channel. ajj7060: I used both async and sync in order supports some of my app using legacy UI. I need both of them work, so I will move to the latest Csla 5.3. There will be a huge job to do. Anyway, in my Csla 5.3.1 sample, the console app works fine, but the Xamarin Form (both iOS and android) failed with the exception during deserialization: This problem is described [here] (#1630). I don't know it is a bug or incorrect configuration. But i'm going to post a minimum project which help to reliable duplicate the issue. |
Beta Was this translation helpful? Give feedback.
I recommend using the http channel, as WCF is not supported at all on modern .NET platforms.