Exposing data object through a service? - wcf

I created a SOAP service that is supposed to return a Category object and a CategoryCollection through a SubSonic query. The problem is that I can return a DataTable of data just fine, however the objects returned from the service are of active record type, internally, and not of my DAL entities.
For example when I consume the service, I see SOAPService.Category, but not SOAPService.CategoryCollection (I should be able to see SOAPService.[all other data entities], and the SOAPService.Category is of active record type, and doesn't contain the actual Category properties.
Both classes defined through my SubSonic DAL generation.
namespace TrainingWebService.DAL
{
/// <summary>
// Strongly-typed collection for the Category class.
/// </summary>
[Serializable]
public partial class CategoryCollection : ActiveList<Category, CategoryCollection>
{
.
.
.
and
/// <summary>
/// This is an ActiveRecord class which wraps the Categories table.
/// </summary>
[Serializable]
public partial class Category : ActiveRecord<Category>, IActiveRecord
{
.
.
.
These classes exist in the TrainingWebService solution.
TrainingWebService.ISOAPService.cs:
using VRPCTrainingWebService.DAL;
namespace TrainingWebService {
// VRPC Training Web Service - Interface
[ServiceContract]
public interface ISOAPService
{
[OperationContract]
string GetDBConnectionStringDetails();
[OperationContract]
string ReturnSameString(string someString);
//
// Database-related
//
[OperationContract] // categories
CategoryCollection GetAllCategories(); // SubSonic object
[OperationContract]
DataTable GetAllCategoriesAsDataTable();
[OperationContract]
DataTable GetCategoryAsDataTable(int id);
[OperationContract]
Category GetCategoryByID(int id); // SubSonic object
[OperationContract]
// products
ProductCollection GetAllProducts();
[OperationContract]
Product GetProductByID(int id);
[OperationContract] // suppliers
SupplierCollection GetAllSuppliers();
[OperationContract]
Supplier GetSupplierByID(int id);
}
}
In my SOAPService.cs
public CategoryCollection GetAllCategories() // get a collection of all categories
{
return DataCaller.GetAllCategories();
}
public DataTable GetAllCategoriesAsDataTable()
{
return DataCaller.GetCategoriesAsDataTable();
}
public DataTable GetCategoryAsDataTable(int id)
{
return DataCaller.GetCategoryAsDataTable(id);
}
Here's a snip of the DataCaller code.
/// <summary>
/// Get all categories - returns a collection of categories
/// </summary>
/// <returns></returns>
public static CategoryCollection GetAllCategories()
{
categoryCollection =
DB.Select().From("Categories")
.ExecuteAsCollection<CategoryCollection>();
return categoryCollection;
}
public static DataTable GetCategoryAsDataTable(int id)
{
try
{
dtResults = new Select()
.Where(Category.Columns.CategoryID).IsEqualTo(id)
.From(Category.Schema)
.ExecuteAsCollection<CategoryCollection>().ToDataTable();
}
catch (Exception ex)
{
throw ex;
}
return dtResults;
}
I think the problem may be in exposing the *.DAL entities through my web service, so they are accessible.
I have this working just fine in another solution I built a while back, but for some reason I can't see what I'm missing here.

Don't forget to decorate your DAL entites with [DataContract], if applicable.
DataContractAttribute Class

Related

WCF web service not working Due to type of Data contract

When i am testing my WCF web service through "WcfTestClient", it is showing
"this operation is not supported in wcf test client because it uses type VINDescription"
Where VINDescriptionis a DataContract, which is consist of datamembers of type :
"int, string, ArrayList"
It seems WCF web service is not supporting ArrayList?
Please suggest how can i fix this?
Here is a code snippet of DataContract :
[DataContract]
public class VINDescription
{
#region Private Members
private int _cylinders = 0;
private string _msrp = string.Empty;
private ArrayList _interior = new ArrayList();
private string[][] _showOptionalEquipment = new string[][] { };
#endregion
#region Public Data Members
/// <summary>
/// Stores the number of cylinders of a decoded vehicle.
/// </summary>
[DataMember]
public int Cylinders
{
get
{
return _cylinders;
}
set
{
_cylinders = value;
}
}
/// <summary>
/// Stores the MSRP cost of a decoded vehicle.
/// </summary>
[DataMember]
public string MSRP
{
get
{
return _msrp;
}
set
{
_msrp = value;
}
}
/// <summary>
/// Stores the interior values of a decoded vehicle.
/// </summary>
[DataMember]
public ArrayList Interior
{
get
{
_interior.Sort();
return _interior;
}
set
{
_interior = value;
}
}
/// <summary>
/// To store the data for show optional equipments.
/// </summary>
[DataMember]
public string[][] ShowOptionalEquipment
{
get
{
return _showOptionalEquipment;
}
set
{
_showOptionalEquipment = value;
}
}
The way I understand it, WCF actually supports your data contract, but the WCF Test Client tool does not support everything that WCF itself supports, hence the error. Not sure if it's because of ArrayList, string[][], or something else, but in any case it seems to be a tool limitation, not a framework limitation.

How to properly implement the Strategy Pattern with two interfaces?

I have created a service data access layer where there are multiple databases where data needs to come from.
I was doing fine with one database where I defined the memberRepository that contained member details. However, now I have to get session-related details that are stored in another database.
OprationContracts:
IMemberServices contains GetLoggedInBuddies(int profileID);
ISessionServices contains GetProfileIDFromSessionID(string sessionID);
My service class:
public class MemberService : IMemberService, ISessionServices
{
#region Strategy pattern configuration
//
// Member repo
//
private MemberRepository memberRepository;
public MemberService()
: this(new MemberRepository())
{ }
public MemberService(MemberRepository memberRepository)
{
this.memberRepository = memberRepository;
}
//
// Session repo
//
private SessionRepository sessionRepository;
public MemberService() : this(new SessionRepository()){}
public MemberService(SessionRepository sessionRepository)
{
this.sessionRepository = sessionRepository;
}
#endregion
/// <summary>
/// Session-related details are maintained in the Secondary database
/// </summary>
/// <param name="sessionID"></param>
/// <returns></returns>
public int GetProfileIDFromSessionID(string sessionID)
{
int sessionProfileID = sessionRepository.GetProfileDetailsFromSessionID(sessionRepository);
return sessionProfileID;
}
/// <summary>
/// Try profileID = 1150526
/// </summary>
/// <param name="profileID"></param>
public void GetLoggedInBuddies(int profileID)
{
memberRepository.GetLoggedInBuddies(profileID);
//return memberRepository.GetLoggedInBuddies(profileID);
}
The issue is that in the // Session Repo section, as I already have a constructor defined. I get that.
So basically in each method I want to do something like
MemberService useSessionRepo = new MemberService(SessionRepository);
useSessionRepo.GetProfileDetailsFromSessionID(...);
MemberService useMemberRepo = new MemberService(MemberRepository);
useMemberRepo.GetLoggedInBuddies(...);
Just need a hand putting this together.
Thanks.
I'm not sure about your issue, but you can use a ctor without param, and with param for each repo.
public MemberService()
{
this.memberRepository = new MemberRepository();
this.sessionRepository = new SessionRepository();
}
I created a central repository that accepts the name of the connection string of the database I want to connect to.
public abstract class DatabaseRepository : BaseRepository
{
static IDbConnection connection;
/// <summary>
/// Handles db connectivity as Dapper assumes an existing connection for all functions
/// Since this app uses three databases, pass in the connection string for the required db.
/// </summary>
/// <returns></returns>
protected static IDbConnection OpenConnection(string connectionStringName)
{
try
{
connection = new SqlConnection(WebConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString);
//connection = SqlMapperUtil.GetOpenConnection(connectionStringName); // if we want to use the Dapper utility methods
connection.Open();
return connection;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // uses singleton for logging
return null;
}
}
.
.
.
Then in my service library, I make the connection to the appropriate db and perform whatever queries I need:
using (IDbConnection connection = OpenConnection("FirstDBConnectionString")) { ...

Temporarily turn off identity column with Fluent AutoMap?

I have begun to test Fluent NHibernate in C#
I have a well normalized object structure with 20 related classes.
I currently use Fluent 1.3 with NHibernate 3.2.
So far I have managed to use the AutoMap feature which suits me fine,
Very convenient!
BUT ...
3 of the tables are "enum tables" that need to have their records set with specific Id value.
I tried to make manual mappings of these tables and let the rest be automapped.
But when the manual table is created it fails because it references a table that is automapped (and not available for manual mapper?)
Is it possible to use AutoMapping but for some very few classes override identity creation on primary key?
I tried to make a custom convention but without success.
public class OverrideIdentityGeneration : Attribute
{
}
public class ConventionIdentity : AttributePropertyConvention<OverrideIdentityGeneration>
{
protected override void Apply(OverrideIdentityGeneration attribute, IPropertyInstance instance)
{
instance.Generated.Never();
}
}
Is there some other way?
It would be sad to be forced back to use manual mapping for all classes ....
class MyIdConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
if (instance.EntityType == ...)
{
instance.GeneratedBy.Assigned();
}
}
}
Update:
for enum-like classes it's often easier to define an enum as id
class ConfigValue
{
public virtual Config Id { get; set; }
}
// the convention is easy
if (instance.EntityType.IsEnum)
{
instance.GeneratedBy.Assigned();
// to save as int and not string
instance.CustomType(typeof(Config));
}
// querying without magic int values
var configValue = Session.Get<ConfigValue>(Config.UIColor);
I used the idea given by Fifo and extended it to use a custom attribute instead.
To make code readable and avoid redundance when using similar idea in other conventions I added an extension method to check for custom attribute.
This is the code I ended up with:
/// <summary>
/// Convention to instruct FluentNHIbernate to NOT generate identity columns
/// when custom attribute is set.
/// </summary>
public class ConventionIdentity : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
if(instance.CustomAttributeIsSet<NoIdentity>())
instance.GeneratedBy.Assigned();
}
}
/// <summary>
/// Custom attribute definition.
/// </summary>
public class NoIdentity : Attribute
{
}
/// <summary>
/// Example on how to set attribute.
/// </summary>
public class Category
{
[NoIdentity]
public int Id { get; set; }
public string Name { get; set; }
}
public static class IInspectorExtender
{
/// <summary>
/// Extender to make convention usage easier.
/// </summary>
public static T GetCustomAttribute<T>(this IInspector instance)
{
var memberInfos = instance.EntityType.GetMember(instance.StringIdentifierForModel);
if(memberInfos.Length > 0)
{
var customAttributes = memberInfos[0].GetCustomAttributes(false);
return customAttributes.OfType<T>().FirstOrDefault();
}
return default(T);
}
}

WCF Services Rest Call

I have a WCF Service callable in SOAP and REST.
If a make SOAP call this works properly, but with REST I have problems.
In summary, the method return POCO ENTITY, but when I call I get a connection error canceled.
The same thing not happens if I call another method that returns a boolean or a string (ie native types).
The error seemed to me that POCO entity that I'm using was not really (that's what I'm using Devart so pretty sure it is).
So what I did, I created a custom map of it (with same property) and i have used AutoMapper to do mapping.
The problem is still there :-(
This is the .svc.cs
public List<GetLuoghiSimiliByAddressesDTO> GetLuoghiSimiliByAddress(string toponimo, string nomestrada, string civico, int idcomune)
{
Agile.SL.Services.IAnagraficaService srv = new Agile.SL.Services.Impl.AnagraficaService();
List<GetLuoghiSimiliByAddressesDTO> result = new List<GetLuoghiSimiliByAddressesDTO>();
Mapper.CreateMap<DTOGetLuoghiSimiliByAddress, GetLuoghiSimiliByAddressesDTO>();
foreach (var dto in srv.GetLuoghiSimiliByAddress(toponimo, nomestrada, civico, idcomune).ToList<DTOGetLuoghiSimiliByAddress>())
{
GetLuoghiSimiliByAddressesDTO newdto = Mapper.Map<DTOGetLuoghiSimiliByAddress, GetLuoghiSimiliByAddressesDTO>(dto);
result.Add(newdto);
}
return result;
}
result contains properly my list of objects.
This is svc
[OperationContract]
[WebGet(UriTemplate = "GetLuoghiSimiliByAddress?Toponimo={toponimo}&Nome_Strada={nomestrada}&Civico={civico}&Id_Comune={idcomune}",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
List<GetLuoghiSimiliByAddressesDTO> GetLuoghiSimiliByAddress(string toponimo, string nomestrada, string civico, int idcomune);
Work properly with this method and operation contract
public bool IsUserAlreadyRegistered(string email)
{
Agile.SL.Services.IAnagraficaService srv = new Agile.SL.Services.Impl.AnagraficaService();
return srv.CheckEmailExistance(email);
}
[OperationContract]
[WebGet(UriTemplate = "IsUserAlreadyRegistered?Email={email}",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
bool IsUserAlreadyRegistered(string email);
this is GetLuoghiSimiliByAddressesDTO
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
namespace Merqurio.Agile.DL.Model.Entities
{
[DataContract(IsReference = true)]
[Serializable]
public class GetLuoghiSimiliByAddressesDTO
{
private int _Id_Luogo;
private string _Toponimo;
private string _Nome_Strada;
private string _Civico;
public GetLuoghiSimiliByAddressesDTO()
{
}
/// <summary>
/// There are no comments for Id_Luogo in the schema.
/// </summary>
[DataMember(Order=1)]
public int Id_Luogo
{
get
{
return this._Id_Luogo;
}
set
{
if (this._Id_Luogo != value)
{
this._Id_Luogo = value;
}
}
}
/// <summary>
/// There are no comments for Toponimo in the schema.
/// </summary>
[DataMember(Order=2)]
public string Toponimo
{
get
{
return this._Toponimo;
}
set
{
if (this._Toponimo != value)
{
this._Toponimo = value;
}
}
}
/// <summary>
/// There are no comments for Nome_Strada in the schema.
/// </summary>
[DataMember(Order=3)]
public string Nome_Strada
{
get
{
return this._Nome_Strada;
}
set
{
if (this._Nome_Strada != value)
{
this._Nome_Strada = value;
}
}
}
/// <summary>
/// There are no comments for Civico in the schema.
/// </summary>
[DataMember(Order=4)]
public string Civico
{
get
{
return this._Civico;
}
set
{
if (this._Civico != value)
{
this._Civico = value;
}
}
}
}
}
Please help me!
You can't use "IsReference = true" on your DataContract.
From MSDN :
Use the IsReference property to instruct the DataContractSerializer to
insert XML constructs that preserve object reference information.
You are returning JSON, not XML...
Anyways you don't need it here, I don't see any circular dependency.
What is the error code you get back when you make the REST call. Also try to enable tracing on your service to see why the request fails via REST. To enable tracing follow this link
Also try to use fiddler to inspect your request that is being made to your service.

Problem with DataContract and hierarchy on WCF

i have a problem with an object in my wcf project.
I have lets say this object:
[DataContract(Name="ClassA")]
public class Person{
//---attributes---
}
[DataContract(Name="ClassB")]
public class Men : Person{
//---attributes---
}
Where ClassB is child of ClassA on the other side.
Then i have a method that is post:
[OperationContract]
[WebInvoke(UriTemplate= "Person", ResponseFormat = WebMessageFormat.Json, Method= "POST")]
public string PostPerson(Person person) {
if(person is Men){
//code...
}
}
The thing is that i receive the person (in the other side, they sendme as a ClassB) but the person is Men returns false.. why?
You need to add the [ServiceKnownType(typeof(Men))] attribute to the PostPerson method.
As Ryan Gross mentions, you need Men to be a known type. Here's a similar question/answer here on SO. One option not mentioned in the linked article is the KnownType attribute. Here's an example of code I've used in the past. The prerequisite is that this class is the base class for all of your data contracts and all of your data contracts are in the same assembly:
/// <summary>
/// Base class for all data contracts.
/// </summary>
[DataContract(Name = "Base", Namespace = "your namespace")]
[KnownType("GetKnownTypes")]
public class BaseDC : IExtensibleDataObject
{
#region Constants and Fields
/// <summary>
/// Instance used to control access to the known types list.
/// </summary>
private static readonly object _knownTypesLock = new object();
/// <summary>
/// Classes derived from this class. Needed to ensure proper functioning of the WCF data
/// constract serializer.
/// </summary>
private static List<Type> _knownTypes;
#endregion
#region Properties
/// <summary>
/// Gets or sets an <c>ExtensionDataObject</c> that contains data that is not recognized as belonging to the
/// data contract.
/// </summary>
public ExtensionDataObject ExtensionData { get; set; }
#endregion
#region Public Methods
/// <summary>
/// Enumerates the types in the assembly containing <c>BaseDC</c> that derive from <c>BaseDC</c>.
/// </summary>
/// <returns>List of <c>BaseDC</c>-derived types.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Not appropriate for a property.")]
public static IEnumerable<Type> GetKnownTypes()
{
lock (_knownTypesLock)
{
if (_knownTypes == null)
{
_knownTypes = new List<Type>();
Assembly contractsAssembly = Assembly.GetAssembly(typeof(BaseDC));
Type[] assemblyTypes = contractsAssembly.GetTypes();
foreach (Type assemblyType in assemblyTypes)
{
if (assemblyType.IsClass && !assemblyType.IsGenericType)
{
if (assemblyType.IsSubclassOf(typeof(BaseDC)))
{
_knownTypes.Add(assemblyType);
}
}
}
_knownTypes.Add(typeof(BaseDC));
}
return _knownTypes;
}
}
#endregion
}