my method look like this:
public List<CivarTransporteService.Model.Cliente> getClientes()
{
using (CivarTransporteService.Model.CivarTransporteModelContainer context = new Model.CivarTransporteModelContainer())
{
return context.Cliente.ToList();
}
}
and my cs:
public interface ICatalogsService
{
[OperationContract]
List<CivarTransporteService.Model.Cliente> getClientes();
}
actually getClientes return all the fields of Clientes database but i just need the name of the client
how could i do this? thx
You can use Linq Select extension method to get only the Name column. you need to update your method signature to return a list of string now.
Assuming Name is a property of the Client entity
public List<string> getClientes()
{
using (var context = new Model.CivarTransporteModelContainer())
{
return context.Cliente.Select(x=>x.Name).ToList();
}
}
And the interface signature as well
public interface ICatalogsService
{
[OperationContract]
List<string> getClientes();
}
Or if you do not want to update the existing interface and it's implementation, you can do this at whererever you are calling it.
ICatalogsService catalogService;
catalogService = new SomeConcreteCatalogService(); // not the best to "new" it up.
// But that is not the real question here.
var clientNameList = catalogService.getClients().Select(s=>s.Name).ToList();
Just to expand a little upon #Shyju's answer in case you're interested in more than just 1 field. I did a similar thing when returning database objects over WCF Data Service. I didn't want the entire database model being returned, just a subset of data, so I created an public version of the class:
[DataContract]
public class PublicCliente
{
[DataMember(Order = 1)]
public int Id;
[DataMember(Order = 2)]
public string Name;
public PublicCliente(Cliente c)
{
Id = c.Id;
Name = c.Name;
}
}
Then in the data service method, I did this:
return context.Cliente.Select(
c => new PublicCliente(c)).ToList();
Related
I am creating a new project to practice by my self, I am looking to return a list using GetAll method, but I'm struggling with it. I'll show the code here so hopefully someone can help.
I am using ASP.NET Core 6 and SQL Server for the data.
namespace Northwind.Web.Data.Repositories.Regions
{
public interface IRegionRespository
{
Task<bool> Create(CreateRegionViewModel region);
Task<bool> Update(RegionViewModel region);
Task<RegionViewModel> GetById(int id);
Task<IEnumerable<RegionViewModel>> GetAll();
}
}
async Task<IEnumerable<RegionViewModel>> IRegionRespository.GetAll()
{
var regionList = _nordwinContext.Region.ToList();
return regionList;
//var query = _nordwinContext.Region.AsNoTracking().AsQueryable();
}
public class Region
{
public int RegionId { get; set; }
public string RegionDescription { get; set; }
}
As mentioned in the comments you need to add your code as text and provide any errors you get in order for anyone to help your properly.
However, I'm going to take a stab in the dark and say that the images you uploaded indicates that you're neglecting to return the actual value of your method, or the list. You need to add the return keyword with the value you get from your data context.
async Task<IEnumerable<RegionViewModel>> GetAll()
{
var regionList = _nordwindContext.Region.Tolist();
return regionList;
}
Edit 1 (received further details of the error)
The method GetAll() wants to return a type RegionViewModel, but your data context is returning the type Region. In order to get around that you should change one of them. Let's try with the Interface and implementation.
async Task<IEnumerable<RegionViewModel>> IRegionRespository.GetAll()
{
var list = _nordwinContext.Region
.Select(r => new RegionViewModel
{
RegionDescription = r.RegionDescription,
RegionId = r.RegionId
}).ToList();
return list;
//var regionList = _nordwinContext.Region.ToList();
//return regionList;
}
I have a simple scenario using the Entity Framework in C#. I have an Entity Post:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In my PostManager I have these methods:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
res.Validate();
db.SaveChanges();
return res.Id;
}
}
public void UpdatePost(int postId, string newName, string newDescription)
{
using (var db = new DbContext())
{
var data = (from post in db.Posts.AsEnumerable()
where post.Id == postId
select post).FirstOrDefault();
data.Name = newName;
data.Description = newDescription;
data.Validate();
db.SaveChanges();
}
}
The method validate() refers to class:
public static class Validator
{
public static void Validate(this Post post)
{
if ( // some control)
throw new someException();
}
I call the validate method before the savechanges() but after adding the object to the context. What's the best practice to validate data in this simple scenario? It's better validate the arguments instead? What's happen to object post if the validate method throw exception after adding the object to the context?
UPDATE:
I have to throw a custom set of exception depending on data validation error.
I strongly recommend you to (if at all possible) to modify your entity so the setters are private (don't worry, EF can still set them on proxy creation), mark the default constructor as protected (EF can still do lazy loading/proxy creation), and make the only public constructors available check the arguments.
This has several benefits:
You limit the number of places where the state of an entity can be changed, leading to less duplication
You protect your class' invariants. By forcing creation of an entity to go via a constructor, you ensure that it is IMPOSSIBLE for an object of your entity to exist in an invalid or unknown state.
You get higher cohesion. By putting the constraints on data closer to the data itself, it becomes easier to understand and reason about your classes.
You code becomes self-documenting to a higher degree. One never has to wonder "is it OK if I set a negative value on this int property?" if it is impossible to even do it in the first place.
Separation of concerns. Your manager shouldn't know how to validate an entity, this just leads to high coupling. I've seen many managers grow into unmaintainable monsters because they simply do everything. Persisting, loading, validation, error handling, conversion, mapping etc. This is basically the polar opposite of SOLID OOP.
I know it is really popular nowadays to just make all "models" into stupid property bags with getters and setters and only a default constructor because (bad) ORMs have forced us to do this, but this is no longer the case, and there are so many issues with this imo.
Code example:
public class Post
{
protected Post() // this constructor is only for EF proxy creation
{
}
public Post(string name, string description)
{
if (/* validation check, inline or delegate */)
throw new ArgumentException();
Name = name;
Description = description;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
}
Then your PostManager code becomes trivial:
using (var db = new DbContext())
{
var post = new Post(name, description); // possibly try-catch here
db.Posts.Add(post);
db.SaveChanges();
return post.Id;
}
If the creation/validation logic is extremely intricate this pattern lends itself very well for refactoring to a factory taking care of the creation.
I would also note that encapsulating data in entities exposing a minimal state-changing API leads to classes that are several orders of magnitude easier to test in isolation, if you care at all about that sort of thing.
As I mentioned in the comments above, you might want to check out .NET System.ComponentModel.DataAnnotations namespace.
Data Annotations (DA) allows you to specify attributes on properties to describe what values are acceptable. It's important to know that DA is completely independent of databases and ORM APIs such as Entity Framework so classes decorated with DA attributes can be used in any tier of your system whether it be the data tier; WCF; ASP.NET MVC or WPF.
In the example below, I define a Muppet class with a series of properties.
Name is required and has a max length of 50.
Scaryness takes an int but it must be in the range of {0...100}.
Email is decorated with an imaginary custom validator for validating strings that should contain an e-mail.
Example:
public class Muppet
{
[Required]
[StringLength(50)]
public string Name {get; set;}
public Color Color {get; set; }
[Range(0,100)]
public int Scaryness {get; set; }
[MyCustomEmailValidator]
public string Email {get;set; }
}
In my project I have to throw customException when i validate the data. It's possible do it using Data Annotations?
Yes you can. To validate this object at any time of your application (regardless of whether it has reached EF or not) just perform this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
.
.
.
Post post = ... // fill it in
Validator.Validate(post);
public static class Validator
{
public static void Validate(this Post post)
{
// uses the extension method GetValidationErrors defined below
if (post.GetValidationErrors().Any())
{
throw new MyCustomException();
}
}
}
public static class ValidationHelpers
{
public static IEnumerable<ValidationResult> GetValidationErrors(this object obj)
{
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(obj, null, null);
Validator.TryValidateObject(obj, context, validationResults, true);
return validationResults;
}
.
.
.
If you want to get the validation error messages you could use this method:
/// <summary>
/// Gets the validation error messages for column.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public static string GetValidationErrorMessages(this object obj)
{
var error = "";
var errors = obj.GetValidationErrors();
var validationResults = errors as ValidationResult[] ?? errors.ToArray();
if (!validationResults.Any())
{
return error;
}
foreach (var ee in validationResults)
{
foreach (var n in ee.MemberNames)
{
error += ee + "; ";
}
}
return error;
}
The free set of steak knives is that the validation attributes will be detected once the object reaches EF where it will be validated there as well in case you forget or the object is changed since.
I think you should be working with Data Annotation as #Micky says above. Your current approach is validating manually after it has been added.
using System.ComponentModel.DataAnnotations;
// Your class
public class Post
{
[Required]
public int Id { get; set; }
[Required,MaxLength(50)]
public string Name { get; set; }
[Required,MinLength(15),MyCustomCheck] // << Here is your custom validator
public string Description { get; set; }
}
// Your factory methods
public class MyFactory() {
public bool AddPost() {
var post = new Post() { Id = 1, Name = null, Description = "This is my test post"};
try {
using (var db = new DbContext()) {
db.Posts.Add(post);
db.SaveChanges();
return true;
}
} catch(System.Data.Entity.Validation.DbEntityValidationException e) {
Console.WriteLine("Something went wrong....");
} catch(MyCustomException e) {
Console.WriteLine(" a Custom Exception was triggered from a custom data annotation...");
}
return false;
}
}
// The custom attribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MyCustomCheckAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value instanceof string) {
throw new MyCustomException("The custom exception was just triggered....")
} else {
return true;
}
}
}
// Your custom exception
public class MyCustomException : Exception() {}
See also:
DbEntityValidationException class: https://msdn.microsoft.com/en-us/library/system.data.entity.validation.dbentityvalidationexception(v=vs.113).aspx
Default data annotations
http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx
Building your custom data annotations (validators):
https://msdn.microsoft.com/en-us/library/cc668224.aspx
I always use two validations:
client side - using jQuery Unobtrusive Validation in combination with Data Annotations
server side validation - and here it depends on application - validation is performed in controller actions or deeper in business logic. Nice place to do it is to override OnSave method in your context and do it there
Remember that you can write custom Data Annotation attributes which can validate whatever you need.
You can modify the code in this way:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
if(res.Validate())
{
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
db.SaveChanges();
return res.Id;
}
}
else
return -1; //if not success
}
public static bool Validate(this Post post)
{
bool isValid=false;
//validate post and change isValid to true if success
if(isvalid)
return true;
}
else
return false;
}
After adding data to DbContext and before calling SaveChanges() you can call GetValidationErrors() method of DbContext and check its count to quiqckly check if there are any errors. You can further enumerate all of errors and get error details against each of them. I have bundled Error conversion from ICollection to string in GetValidationErrorsString() extension method.
if (db.GetValidationErrors().Count() > 0)
{
var errorString = db.GetValidationErrorsString();
}
public static string GetValidationErrorsString(this DbContext dbContext)
{
var validationErrors = dbContext.GetValidationErrors();
string errorString = string.Empty;
foreach (var error in validationErrors)
{
foreach (var innerError in error.ValidationErrors)
{
errorString += string.Format("Property: {0}, Error: {1}<br/>", innerError.PropertyName, innerError.ErrorMessage);
}
}
return errorString;
}
I have a class Customers. I want to put some validations on it.
e.g. CustGuidId is not Guid.Empty, CustName is NOT NULL (Required).
public class Customer
{
public int CustId;
public string CustName;
public Guid CustGuid;
public Guid[] OrderGuids;
}
I have such collection of customers. So I have ended up adding code like this, which makes it look ugly.
public class BatchError
{
public int Index;
public string ErrorCode;
public string ErrorMessage;
}
public void GenerateValidationErrors(List<Customer> customers, out List<BatchError> batchErrors)
{
int rowNum = 0;
batchErrors = new List<BatchError>(customers.Count);
foreach (var customer in customers)
{
rowNum ++;
Guid customerGuidParsed;
if(!Guid.TryParse(customer.CustGuid.ToString(), out customerGuidParsed))
{
batchErrors.Add(new BatchError { Index = rowNum, ErrorCode = "CustomerGuidcannotBeNull", ErrorMessage = "Customer guid cannot be null." });
}
if (string.IsNullOrEmpty(customer.CustName))
{
batchErrors.Add(new BatchError { Index = rowNum, ErrorCode = "CustomerNamecannotBeEmpty", ErrorMessage = "Customer Name cannot be empty." });
}
}
}
Can we write separate validator classes, like GuidValidator, StringValidator.
and Create array of delegates & chain their invokes ?
(Customer c) => new GuidValidator(c.CustGuid.toString()),
(Customer c) => new StringValidator(c.CustName.toString())
But what design pattern would be best suitable for this scenario?
Is there any other way to add validations in WCF?
There are many ways to do the validation. I prefer to validate DataContract itself before any action.
It can also be done in many like :
DatamemberAttribute has many properties. One of them is
IsRequired,it controls the minOccurs attribute for the schema
element. The default value is false. You can use it like:
[DataContract(Name ="Place", Namespace ="")]
public class DataContractExample
{
[DataMember(IsRequired=true)]
public string DataMemberExample;
}
For more information refer: DataMemberAttribute Class on MSDN.
Easiest way is to validate property like:
[DataContract]
public class Customer
{
[DataMember]
public string CustName
{
get
{
return this._custName;
}
set
{
if(string.IsNullOrEmpty(value))
throw new MyValidationException();
else
this._custName=value;
}
}
}
Another way can be to use Microsoft Enterprise Library. In order to enable validation of the properties of a request message, you only need to add a [ValidationBehavior] attribute to your service interface, just next (or before) the [ServiceContract], and a [FaultContract(typeof(ValidationFault))] on the method declaration. The ValidationBehaviorAttribute and ValidationFault classes are defined in the Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF assembly and are part of the Validation Application Block of the Enterprise Library 4.1, more specifically, of the WCF integration module. See full implementation in detail at: http://weblogs.asp.net/ricardoperes/validation-of-wcf-requests-with-the-enterprise-library-validation-block
Finally one more solution cane be to use WCF Data Annotations from http://wcfdataannotations.codeplex.com/. Using this you can use validations like:
[DataMember]
[Required]
[StringLength(500, MinimumLength = 5)]
public string Description{ get; set; }
Choose which ever suite your requirements. Cheers.
I have standard XML data coming in that represents a purchase order from a customer. Each customer will populate the XML data differently so I need a separate method to process the order based on their specifications. My goal is to make this scalable so I used an interface because I would like to be able to create additional classes as new customers are added.
How do I select a different Map class based on the customer?
public class XmlPurchaseOrder
{
public DateTime Created { get; set; }
public string CustomerId { get; set; }
public string PurchaseOrderId { get; set; }
public string MapName { get; set; }
//...
}
public interface IXmlMapper
{
CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po);
}
public class CustomerOrder
{
public int Id { get; set; }
public string CustomerId { get; set; }
public string CustomerPoId { get; set; }
public DateTime OrderDate { get; set; }
}
//Maps by customer
public class McClownMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "McD123",
CustomerPoId = po.PurchaseOrderId,
OrderDate = DateTime.Today
};
}
}
public class BkMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "BxK331",
CustomerPoId = string.Format("BxK{0}", po.PurchaseOrderId),
OrderDate = DateTime.Today.AddDays(-1)
};
}
}
public class TacoWorldMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "TW-33",
CustomerPoId = string.Format("{0}-{1}",po.PurchaseOrderId, DateTime.Now.Ticks),
OrderDate = po.Created
};
}
}
class Program
{
private static void Main(string[] args)
{
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//How do I create the associated class by the MapName specified.
IXmlMapper t = Activator.CreateInstance(Type.GetType(po.MapName));
var customerOrder = t.MapToCustomerOrder(po);
//...
}
}
Thanks
Perhaps you could split the workload, so that your Deserializer decorates the XmlPurchaseOrder with a PurchaseOrderType (enum) based on the characteristics that determines the purchase order type. If this is determined by the XML structure itself, like via a tag or an attribute, this is a simple task - otherwise subclass the XmlPurchaseOrder and introduce a virtual method that "calculates" the type.
The other part of the job is to instantiate the concrete PurchaseOrder - this can be simplified using a Factory with one Create method for each kind of purchase order, or more brute force with a big switch on the PurchaseOrderType enum.
A very simple way would be to add a config setting for each customer that maps to the type used to process their order.
<appSettings>
<add key="Customer1" value="MyApp.Logic.Customer1Processor" />
<add key="Customer2" value="MyApp.Logic.Customer2Processor" />
//etc...
</appSettings>
then use Activator.CreateInstance like you have currently.
This makes me think of the Provider Model available through .Net. I am currently using it to instantiate different API Providers based on their Provider Type.
You can set up a near infinite number of different classes that inherit from ProviderBase and add whatever methods you will need to this class. Then, you create each .dll to perform whatever functionality you need and since they have all inherited from some similar base class, you can put the primary method to begin processing the functionality in there.
Base class:
namespace ProviderManager
{
abstract public class SendProviderBase : ProviderBase
{
abstract public void Process(whatever args you need);
}
}
Helper class used to instantiate different Providers
namespace ProviderManger
{
public class ProviderManger
{
private ConfigHandler sendConfig;
public ProviderManger()
{
sendConfig = ConfigurationManger.GetSection("sendProvider") as ConfigHandler;
}
public SendProviderBase GetSendProviderBase(string MapName)
{
try
{
ProviderSettings settings = sendConfig.Providers[MapName];
return (SendProviderBase)ProvidersHelper.InstantiateProvider(settings, typeof(SendProviderBase));
}
//appropriate catch block and whatever else
}}
ConfigHandler code
namespace ProviderManger
{
class ConfigHandler : ConfigurationSection
{
[ConfigurationProperty("providers")}
public ProviderSettingsCollection Providers
{
get
{ return base["providers"] as ProviderSettingsCollection; }
}}}
Usage in Main for you
providerManager = new ProviderManager();
SendProviderManger provider = providerManager.GetSendProviderBase(MapName);
provider.Process(whatever args...);
Obviously you could rename SendProviderBase to something more related to what you're doing but I kept that name since it was consistent through my code here. The only other thing you'll need is a declaration of the .config section used to store MapNames that map to the .dll that is related to it. Since my application is a web service we have a web.config with the following sections:
Custom Section declaration:
<configSections>
<section name="sendProvider" type="KC.ProviderManager.ConfigHandler, ProviderManager"/>
</configSections>
And the Send Provider section:
<sendProviders>
<providers>
<add name="MapNameX" type="namespace.classname, assemblyname">
So basically what this does is you feed providerManger.GetSendProviderBase(MapNameX) the name in the web.config and it returns to you (assuming everything else is built correctly) the class found in that assembly. Then you can call the method found on the base class to begin processing (provider.Process()).
The other necessary References are as follows
System.Reflection;
System.Configuration;
System.Configuration.Provider;
System.Web.Configuration;
This is highly scalable as you can add as many providers as you want as long as they inherit correctly
Or, for a more simplified but still quite scalable solution similar to this check out this link
I did some further research and what I needed was a Factory. This is my interpretation of a demo in a Pluralsight.com video called Design Patterns Library that was presented by David Starr
public class CustomerMapFactory
{
private Type[] _mapTypes;
public CustomerMapFactory()
{
LoadAvailableMaps();
}
//Return a newly created Type
public IXmlMapper CreateInstance(string customerId)
{
var t = GetTypeToCreate(customerId);
if (t == null) throw new Exception("Customer map not found");
return Activator.CreateInstance(t) as IXmlMapper;
}
//Find the map to instantiate
Type GetTypeToCreate(string customerId)
{
return _mapTypes.FirstOrDefault(tpMap => tpMap.Name.Contains(customerId));
}
//Identify all Types that use the IXmlMapper
private void LoadAvailableMaps()
{
_mapTypes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetInterface(typeof(IXmlMapper).ToString()) != null)
.ToArray();
}
}
}
Here is the program that utilizes the factory
class Program
{
private static void Main(string[] args)
{
//Same as above
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//Now utilizing the factory.
var mf = new CustomerMapFactory();
var poMap = mf.CreateInstance("BkMap");
var customerOrder = poMap.MapToCustomerOrder(po);
}
I am using Domain Service to fetch data from database from Silverlight Client.
In DomainService1.cs, I have added the following:
[EnableClientAccess()]
public class Product
{
public int productID;
public string productName;
public List<Part> Parts = new List<Part>(); //Part is already present in Model designer
}
In DomainService1 class I added a new method to retrive a collection of the custom class object:
[EnableClientAccess()]
public class DomainService1 : LinqToEntitiesDomainService<HELPERDBNEWEntities1>
{
...
public List<Product> GetProductsList(...)
{
List<Product> resultProducts = new List<Product>();
...
return resultProducts;
}
}
From the silverlight client I am trying to access that method:
DomainService1 ds1 = new DomainService1();
var allproductList = ds1.GetProductsList(...);
ds1.Load<SLProduct>(allproductList).Completed += new EventHandler(Load_Completed); //Not correct usage
However it is not the correct way to call the new method. The reason I added a new class Product in DomainServices.cs is to have an efficient grouping. I cannot achieve the same using the model classes auto-generated by the entity framework.
How call I call the new method from the client?
I believe there is a similar question with an answer here:
Can a DomainService return a single custom type?
Also, here is some discussion about the overall problem of adding custom methods in a Domain Service:
http://forums.silverlight.net/t/159292.aspx/1
While I don't know what you mean by "it is not the correct way to call the new method", or if you're getting any errors, I thought maybe posting some working code might help.
My POCO
public class GraphPointWithMeta
{
[Key]
public Guid PK { get; set; }
public string SeriesName { get; set; }
public string EntityName { get; set; }
public double Amount { get; set; }
public GraphPointWithMeta(string seriesName, string entityName, double amount)
{
PK = Guid.NewGuid();
SeriesName = seriesName;
EntityName = entityName;
Amount = amount;
}
// Default ctor required.
public GraphPointWithMeta()
{
PK = Guid.NewGuid();
}
}
A method in the domain service (EnableClientAccess decorates the class)
public IEnumerable<GraphPointWithMeta> CallingActivityByCommercial()
{
List<GraphPointWithMeta> gps = new List<GraphPointWithMeta>();
// ...
return gps;
}
Called from the Silverlight client like
ctx1.Load(ctx1.CallingActivityByCommercialQuery(), CallingActivityCompleted, null);
client call back method
private void CallingActivityCompleted(LoadOperation<GraphPointWithMeta> lo)
{
// lo.Entities is an IEnumerable<GraphPointWithMeta>
}
I am not sure if your Product class is an actual entity or not. From the way it is defined, it does not appear to be an entity. My answer is assuming it is not an entity. You will need to apply the DataMemberAttribute for your Product properties, and you wouldn't load the product list - load is for Entity Queries (IQueryable on the service side). You would just invoke it like this (client side):
void GetProductList( Action<InvokeOperation<List<Product>>> callback)
{
DomainService ds1 = new DomainService();
ds1.GetProductsList(callback, null);//invoke operation call
}
And the domain service's (server side) method needs the InvokeAttribute and would look like this:
[EnableClientAccess]
public class MyDomainService
{
[Invoke]
public List<Product> GetProductList()
{
var list = new List<Product>();
...
return list;
}
}
And here is how your Product class might be defined (if it is not an entity):
public class Product
{
[DataMember]
public int productID;
[DataMember]
public string productName;
[DataMember]
public List<Part> Parts = new List<Part>(); // you might have some trouble here.
//not sure if any other attributes are needed for Parts,
//since you said this is an entity; also not sure if you
//can even have a list of entities or it needs to be an
//entity collection or what it needs to be. You might
//have to make two separate calls - one to get the products
//and then one to get the parts.
}
Like I said, i am not sure what Product inherits from... Hope this helps.