How to insert 2 new related DTOs using RIA Service? - c#

I am using RIA Service in our Silverlight application. Database entities are not directly exposed to a client but I have a set of POCO classes for it. Then in CRUD methods for these POCO classes they are converted to database entities and saved to database.
The problem arises on the server side when client creates 2 new POCO entities which are related. Insert method is called on the server for each POCO entity separately and I may create corresponding new database entities there and add them to object context. But I see no way to add relation between these created database entities. Is there a solution for that?
For example, I have these 2 POCO entities (simplified):
[DataContract(IsReference = true)]
public partial class Process
{
[DataMember]
[Key]
public string Name
{
get; set;
}
[DataMember]
public long StepId
{
get; set;
}
[DataMember]
[Association("StepProcess", "StepId", "Id", IsForeignKey=true)]
public Step Step
{
get; set;
}
}
[DataContract(IsReference = true)]
public partial class Step
{
[DataMember]
[Key]
public long Id
{
get; set;
}
[DataMember]
public string Name
{
get; set;
}
}
And I have these 2 Insert methods in my domain service class:
public void InsertProcess(Process process)
{
var dbProcess = new DBProcess();
dbProcess.Name = process.Name;
//dbProcess.StepId = process.StepId; Cannot do that!
this.ObjectContext.AddToDBProcess(dbProcess);
}
public void InsertStep(Step step)
{
var dbStep = new DBStep();
dbStep.Name = step.Name;
this.ObjectContext.AddToDBSteps(dbStep);
this.ChangeSet.Associate<Step, DBStep>
(step, dbStep, (dto, entity) =>
{
dto.Id = entity.Id;
});
}
Client adds a new Process, then creates and adds a new Step to it and then calls SubmitChanges(). Process.StepId is not filled with a correct value as there is no correct Step.Id for the newly created step yet, so I cannot just copy this value to database entity.
So the question is how to recreate relations between newly created database entities the same as they are in newly created DTOs?
I know about Composition attribute but it is not suitable for us. Both Process and Step are independent entities (i.e. steps may exist without a process).

There are two ways to solve this:
Have each call return the primary key for the item after it is created, then you can store the resulting PKey in the other POCO to call the second service.
Create a Service method that takes both POCOs as parameters and does the work of relating them for you.

Thanks, although both these suggestions are valid but they are also applicable only for simple and small object hierarchies, not my case. I end up using approach similar to this. I.e. I have a POCO to database objects map. If both Process and Step are new, in InsertProcess method process.Step navigation property is filled with this new step (otherwise StepId can be used as it referenced to existing step). So if this process.Step is in the map I just fill corresponding navigation property in DBProcess, otherwise I create new instance of DBStep, put it to the map and then set it to DBProcess.Step navigation property. This new empty DBStep will be filled in InsertStep method later.

Related

Is custom field option a Valueobject or an Entity in Domain Driven Design

I am using EF Core and I have a scenario where the user can create a custom field and then creates options for that custom fields.
public class CustomField : Entity<long>
{
[Required]
public string Name { get; private set; }
public bool IsRequired { get; private set; }
public List<CustomFieldOption> customFieldOptions;
public virtual IReadOnlyCollection<CustomFieldOption> CustomFieldOptions => customFieldOptions;
protected CustomField()
{
}
public CustomField(long id, string name, bool isRequired, List<CustomFieldOption> customFieldOptions)
{
Id = id;
Name = name;
IsRequired = isRequired;
this.customFieldOptions = customFieldOptions;
}
}
public class CustomFieldOption : Entity<long>
{
[Required]
[MaxLength(256)]
public string Text { get; private set; }
protected CustomFieldOption()
{
}
public CustomFieldOption(string text)
{
Text = text;
}
}
public class Client : Entity<long>
{
public Name Name { get; set; }
private List<ClientCustomFieldOptionValue> customFieldOptionValues { get; set; } = new List<ClientCustomFieldOptionValue>();
public IReadOnlyCollection<ClientCustomFieldOptionValue> CustomFieldOptionValues => customFieldOptionValues;
public Client(Name name)
{
}
public Result AddCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
public Result RemoveCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
}
public class ClientCustomFieldOptionValue
{
public CustomFieldOption CustomFieldOption { get; private set; }
protected CustomFieldOptionValue()
{
}
public ClientCustomFieldOptionValue(CustomFieldOption customFieldOption)
{
CustomFieldOption = customFieldOption;
}
}
CustomFieldOption seems to be a Value Object as the text it holds is something that doesn't need an Id. But then in terms of store persistency needs an Id to be stored in database on a different table where it can be queries by Id etc...
I am not sure if I shall add it as an Entity because ValueObjects do not have Id.
One other problem I have is validation. If it is an Entity how can I validate Text property. I know validation on constructor is a bad idea. If I validate it in the ApplicationLayer then wherever I create a new object I have to validate that is not empty and the length.
If I forget to add validation in one of the application services and pass null Text then I create an inconsistent state.
Update #1
A Client can select one or many options of a custom field. I suppose these needed to be stored on a separate table ClientCustomFieldOptionValue. In that case is this an entity or a valueobject? And what about CustomFieldOption. Does it become an Entity? I am quite confused when to use Entity or ValueObjects
Try not to think of persistency details while designing domain model.
According to your description, CustomFieldOption expresses an individual property with no business relations to any other structure, thus:
it should not hold a business identifier
it should encapsulate its own validations
Meaning it fits the concept of a value-object (validation inside ctor).
When it comes to persistency, your repository model should be capable of storing CustomFieldOption objects in a child table (with DB identifier) referencing the parent table (CustomField objects)
On the query side, repository should be capable of aggregating data from these two tables into a single CustomField entity.
(How exactly you implement such DB capabilities depends on the ORM you choose to work with, EF in your case)
Just one observation, if you will use Ef Core and the containing entity has a one to many relationship with the value objects, you will have this limitation:
Owned Entity types, Ef Core
Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance
If you are mapping your entities and value objects using DbContext, you usually define an owned entity type for a value object or use a record type.
For owned entities, this creates a column in your table like this: EntityName_ValueObject (i.e. Person_Address) but this works for a single value object not a collection when you don't know in advance the number of items in the collection.
It is correct that you should not concern with persistence when designing your domain, but is also correct to think that having a value object with an identity does not make sense.
Most important, you should be aware of this potential issue early on.

Entity Framework 6: Adding child object to parent's list vs. setting child's navigation property to parent

I have an existing database with two tables MailServers and MailDomains in it. MailDomains has the foreign key column MailServerId pointing to the Id primary key column in MailServers. So we have a one-to-many-relationship here.
I followed this article and created my Entity Framework POCOs via the "Code first from database" model in the Entity Data Model Wizard. This produced the following two C# classes:
public partial class MailServer
{
public MailServer()
{
MailDomains = new HashSet<MailDomain>();
}
public int Id { get; set; }
public virtual ICollection<MailDomain> MailDomains { get; set; }
}
public partial class MailDomain
{
public MailDomain()
{
}
public int Id { get; set; }
public string DomainName { get; set; }
public int MailServerId { get; set; }
public virtual MailServer MailServer { get; set; }
}
Now my question is whether there is any difference between the following two approaches of creating and inserting new objects to the database.
Approach (A): Adding new child to the parent's list:
var mailServer = new MailServer();
var mailDomain = new MailDomain() {
DomainName = "foobar.net",
};
mailServer.MailDomains.Add(mailDomain);
using(var context = new MyContext){
context.MailServers.Add(mailServer);
context.SaveChanges();
}
Approach (B): Setting the child's navigation property to the parent:
var mailServer = new MailServer();
var mailDomain = new MailDomain() {
DomainName = "foobar.net",
MailServer = mailServer,
};
using(var context = new MyContext){
context.MailDomains.Add(mailDomain);
context.SaveChanges();
}
I also assume that in approach (A) the new MailDomain instance is automatically added to the collection context.MailDomains while in approach (B) the new MailServer instance is automatically added to the collection context.MailServers. Is that correct or do I have to do that manually?
So again, my question is: are the two approaches interchangeable?
It just confuses me that in the database there is only one property/column to set (namely the foreign key in MailDomains) while in the C# code there are two properties (one in each class) that could be modified.
Yes, the two approaches are interchangeable. This allows you to create and save your object graph to the database from either the perspective of the MailServer or the MailDomain.
If you do code-first, you have the option of removing the properties and mappings if they're not needed.
I also assume that in approach (A) the new MailDomain instance is
automatically added to context.MailDomains while in approach (B) the
new MailServer instance is automatically added to context.MailServers.
Is that correct or do I have to do that manually?
It depends what you mean by "added to the context". If you mean: does it automatically get saved to the database when you persist, the answer is yes. One of the big benefits to using an ORM like EF is that it handles saving a full object graph automatically (and syncing PK/FK relations, etc.).
If you mean: will the entity be available via the context before saving, I don't think so (I'm not 100% sure).

WCF crashes after sending EF object with it's references

I have database with several tables, including:
CREATE TABLE [dbo].[Exam]
(
[Id] INT NOT NULL PRIMARY KEY identity,
[Author id] INT NULL,
...
CONSTRAINT [FK_Exam_Author] FOREIGN KEY ([Author id]) REFERENCES [User]([Id]),
)
CREATE TABLE [dbo].[User]
(
[Id] INT NOT NULL PRIMARY KEY identity,
...
)
I have ADO.NET generated model of my database.
public partial class Exam
{
public Exam()
{
...
}
public int Id { get; set; }
public Nullable<int> Author_id { get; set; }
...
public virtual User User { get; set; }
...
}
public partial class Exam
{
public User()
{
this.Exam = new HashSet<Exam>();
...
}
public int Id { get; set; }
...
public virtual ICollection<Exam> Exam { get; set; }
...
}
I have also GetExam function in my Wcf service, which returns one exam from database.
public Exam GetExam()
{
var context = new GDBDatabaseEntities();
context.Configuration.ProxyCreationEnabled = false;
var exams = context.Exam;
var exam = exams.FirstOrDefault();
return exam;
}
Disabling ProxyCreationEnabled was necessary to send Exam through Wcf.
Unfortunately above code returns me an exam with empty User field (EF generated this field automatically as a response for FK_Exam_Author)...
I've tried to load User attribute with Include function:
var exams = context.Exam.Include("User");
I've got following Wcf's error:
Failed to invoke the service. Possible causes: The service is offline or inaccessible; the client-side configuration does not match the proxy; the existing proxy is invalid. Refer to the stack trace for more detail. You can try to recover by starting a new proxy, restoring to default configuration, or refreshing the service.
Just before return from Wcf debugger shows that Exam object looks properly (has loaded User dependency).
I think it could be caused by fact, that loaded User has loaded his Exams list, which has loaded User... (circular dependence).
I've exact the same problem with User - Wcf works for User with empty Exams[] property, but when I've loaded list of User's Exams, Wcf has crashed...
How can I properly load relationship and send it via Wcf?
Additional question is how Wcf know how to serialize my objects (when I've used LINQ to SQL it generated classes with DataContract and DataMember attributes, but Entity Framework just don't do it.
UPDATE
It's the problem with circular dependences, because
public Exam GetExam()
{
var context = new GDBDatabaseEntities();
context.Configuration.ProxyCreationEnabled = false;
var exams = context.Exam.Include("User");
var exam = exams.FirstOrDefault();
exam.User.Exam = null;
return exam;
}
works.
Could anyone explain me why Include function is necessary for load 1-level dependences, which i need, but other dependences are loading for infinity? It's radiculous...
UPDATE
According to the answers and Yahoo's Best Practices for Speeding Up Your Web Site first rule I decided to create new object for every single Page in my Windows 8 application, which will reduce amount of sending data, number of requests and will let me avoid sending redundant data.
Having tried this several times, I quickly abandoned using EF objects as my data contracts. As you found, the circular dependency breaks the DataContractSerializer very quickly. Additionally, your database objects likely contain information that doesn't need to be sent across the wire to clients.
Instead, create similar DataContract objects to the EF generated ones for use with WCF (without the circular dependencies and other problems), and translate between the two. For example:
[DataContract]
public class User
{
[DataMember]
public IEnumerable<Exam> Exams {get; set;}
}
[DataContract]
public class Exam
{
[DataMember]
public int Score {get; set;}
}
Then you just need a loader function like:
IEnumerable<Contracts.User> GetAllUsers()
{
foreach (DB.User in context.Users)
{
Contracts.User wcfUser = new Contracts.User();
wcfUser.Exams = new List<Contracts.Exam>();
foreach (DB.Exam exam in wcfUser.Exams)
wcfUser.Exams.Add(new Contracts.Exam() { Score = exam.Score };
yield return wcfUser;
}
}
Obviously this expands your code-base quite a bit, but avoids having your clients needing to know about your database objects, helps keep your network traffic down, and results in a better separation of concerns.
Hopefully that answers your question! Let me know if I can clarify anything.

Entity Framework associations with multiple (separate) keys on view

I'm having problems setting up an Entity Framework 4 model.
A Contact object is exposed in the database as an updateable view. Also due to the history of the database, this Contact view has two different keys, one from a legacy system. So some other tables reference a contact with a 'ContactID' while other older tables reference it with a 'LegacyContactID'.
Since this is a view, there are no foreign keys in the database, and I'm trying to manually add associations in the designer. But the fluent associations don't seem to provide a way of specifying which field is referenced.
How do I build this model?
public class vwContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class LegacyObject
{
public virtual vwContact Contact { get; set; }
public string ContactId { get; set; } //references vwContact.LegacyKeyField
}
ModelCreatingFunction(modelBuilder)
{
// can't set both of these, right?
modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
modelBuilder.Entity<vwContact>().HasKey(x => x.LegacyKeyField);
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).???
//is there some way to say which key field this reference is referencing?
}
EDIT 2: "New things have come to light, man" - His Dudeness
After a but more experimentation and news, I found using a base class and child classes with different keys will not work by itself. With code first especially, base entities must define a key if they are not explicitly mapped to tables.
I left the suggested code below because I still recommend using the base class for your C# manageability, but I below the code I have updated my answer and provided other workaround options.
Unfortunately, the truth revealed is that you cannot accomplish what you seek without altering SQL due to limitations on EF 4.1+ code first.
Base Contact Class
public abstract class BaseContact
{
// Include all properties here except for the keys
// public string Name { get; set; }
}
Entity Classes
Set this up via the fluent API if you like, but for easy illustration I've used the data annotations
public class Contact : BaseContact
{
[Key]
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class LegacyContact : BaseContact
{
public int KeyField { get; set; }
[Key]
public string LegacyKeyField { get; set; }
}
Using the Entities
Classes that reference or manipulate the contact objects should reference the base class much like an interface:
public class SomeCustomObject
{
public BaseContact Contact { get; set; }
}
If later you need to programmatically determine what type you are working with use typeof() and manipulate the entity accordingly.
var co = new SomeCustomObject(); // assume its loaded with data
if(co.Contact == typeof(LegacyContact)
// manipulate accordingly.
New Options & Workarounds
As I suggested in comment before, you won't be able to map them to a single view/table anyway so you have a couple options:
a. map your objects to their underlying tables and alter your "get/read" methods on repositories and service classes pull from the joined view -or-
b. create a second view and map each object to their appropriate view.
c. map one entity to its underlying table and one to the view.
Summary
Try (B) first, creating a separate view because it requires the least amount of change to both code and DB schema (you aren't fiddling with underlying tables, or affecting stored procedures). It also ensures your EF C# POCOs will function equivalently (one to a view and one to table may cause quirks). Miguel's answer below seems to be roughly the same suggestion so I would start here if it's possible.
Option (C) seems worst because your POCO entities may behave have unforseen quirks when mapped to different SQL pieces (tables vs. views) causing coding issues down the road.
Option (A), while it fits EF's intention best (entities mapped to tables), it means to get your joined view you must alter your C# services/repositories to work with the EF entities for Add, Update, Delete operations, but tell the Pull/Read-like methods to grab data from the joint views. This is probably your best choice, but involves more work than (B) and may also affect Schema in the long run. More complexity equals more risk.
Edit I'm not sure this is actually possible, and this is why:
The assumption is that a foreign key references a primary key. What you've got is two fields which are both acting as primary keys of vwContact, but depending on which object you ask it's a different field that's the primary key. You can only have one primary key at once, and although you can have a compound primary key you can't do primary key things with only half of it - you have to have a compound foreign key with which to reference it.
This is why Entity Framework doesn't have a way to specify the mapping column on the target side, because it has to use the primary key.
Now, you can layer some more objects on top of the EF entities to do some manual lookup and simulate the navigation properties, but I don't think you can actually get EF to do what you want because SQL itself won't do what you want - the rule is one primary key per table, and it's not negotiable.
From what you said about your database structure, it may be possible for you to write a migration script which can give the contact entities a consistent primary key and update everything else to refer to them with that single primary key rather than the two systems resulting from the legacy data, as you can of course do joins on any fields you like. I don't think you're going to get a seamlessly functional EF model without changing your database though.
Original Answer That Won't Work
So, vwContact contains a key KeyField which is referenced by many SomeObjects and another key LegacyKeyField which is referenced by many LegacyObjects.
I think this is how you have to approach this:
Give vwContact navigation properties for SomeObject and LegacyObject collections:
public virtual ICollection<SomeObject> SomeObjects { get; set; }
public virtual ICollection<LegacyObject> LegacyObjects { get; set; }
Give those navigation properties foreign keys to use:
modelBuilder.Entity<vwContact>()
.HasMany(c => c.SomeObjects)
.WithRequired(s => s.Contact)
.HasForeignKey(c => c.KeyField);
modelBuilder.Entity<vwContact>()
.HasMany(c => c.LegacyObjects)
.WithRequired(l => l.Contact)
.HasForeignKey(c => c.LegacyKeyField);
The trouble is I would guess you've already tried this and it didn't work, in which case I can't offer you much else as I've not done a huge amount of this kind of thing (our database is much closer to the kinds of thing EF expects so we've had to do relatively minimal mapping overrides, usually with many-to-many relationships).
As for your two calls to HasKey on vwContact, they can't both be the definitive key for the object, so it's either a compound key which features both of them, or pick one, or there's another field you haven't mentioned which is the real primary key. From here it's not really possible to say what the right option there is.
You should be able to do this with two different objects to represent the Contact view.
public class vwContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class vwLegacyContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class LegacyObject
{
public virtual vwLegacyContact Contact { get; set; }
public string ContactId { get; set; } //references vwLegacyContact.LegacyKeyField
}
ModelCreatingFunction(modelBuilder)
{
// can't set both of these, right?
modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
modelBuilder.Entity<vwLegacyContact>().HasKey(x => x.LegacyKeyField);
// The rest of your configuration
}
I have tried everything that you can imagine, and found that most solutions won't work in this version of EF... maybe in future versions it supports referencing another entity by using an unique field, but this is not the case now. I also found two solutions that work, but they are more of a workaround than solutions.
I tried all of the following things, that didn't work:
Mapping two entities to the same table: this is not allowed in EF4.
Inheriting from a base that has no key definitions: all root classes must have keys, so that inherited classes share this common key... that is how inheritance works in EF4.
Inheriting from base class that defines all fields, including keys, and then use modelBuilder to tell wich base-properties are keys of the derived types: this doesn't work, because the methos HasKey, Property and others that take members as parameters, must reference members of the class itself... referencing properties of a base class is not allowed. This cannot be done: modelBuilder.HasKey<MyClass>(x => x.BaseKeyField)
The two things that I did that worked:
Without DB changes: Map to the table that is source of the view in question... that is, if vwContact is a view to Contacts table, then you can map a class to Contacts, and use it by setting the key to the KeyField, and another class mapping to the vwContacts view, with the key being LegacyKeyField. In the class Contacts, the LegacyKeyField must exist, and you will have to manage this manually, when using the Contacts class. Also, when using the class vwContacts you will have to manually manage the KeyField, unless it is an autoincrement field in the DB, in this case, you must remove the property from vwContacts class.
Changing DB: Create another view, just like the vwContacts, say vwContactsLegacy, and map it to a class in wich the key is the LegacyKeyField, and map vwContacts to the original view, using KeyField as the key. All limitations from the first case also applies: the vwContacts must have the LegacyKeyField, managed manually. And the vwContactsLegacy, must have the KetField if it is not autoincrement idenitity, otherwise it must not be defined.
There are some limitations:
As I said, these solutions are work-arounds... not real solutions, there are some serious implications, that may even make them undesirable:
EF does not know that you are mapping two classes to the same thing. So when you update one thing, the other one could be changed or not, it depends if the objects is cached or not. Also, you could have two objects at the same time, that represents the same thing on the backing storage, so say you load a vwContact and also a vwContactLegacy, changes both, and then try to save both... you will have to care about this yourself.
You will have to manage one of the keys manually. If you are using vwContacts class, the KeyFieldLegacy is there, and you must fill it. If you want to create a vwContacts, and associate is with a LegacyObject, then you need to create the reference manually, because LegacyObject takes a vwContactsLegacy, not a vwContacts... you will have to create the reference by setting the ContactId field.
I hope that this is more of a help than a disillusion, EF is a powerfull toy, but it is far from perfect... though I think it's going to get much better in the next versions.
I think this may be possible using extension methods, although not directly through EF as #Matthew Walton mentioned in his edit above.
However, with extension methods, you can specify what to do behind the scenes, and have a simple call to it.
public class LegacyObject
{
public virtual vwContact Contact { get; set; }
public string ContactId { get; set; } //references vwContact.LegacyKeyField
}
public class LegacyObjectExtensions
{
public static vwContact Contacts(this LegacyObject legacyObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in legacyObject
join c in dbContext.vwContact
on o.ContactId == c.LegacyKeyField
select c;
return contacts;
}
}
and
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class SomeObjectExtensions
{
public static vwContact Contacts(this SomeObject someObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in someObject
join c in dbContext.vwContact
on o.ContactId == c.KeyField
select c;
return contacts;
}
}
Then to use you can simply do like this:
var legacyContacts = legacyObject.Contacts();
var someContacts = someObject.Contacts();
Sometimes it makes more sense to map it from the other end of the relationship, in your case:
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.LegacyKeyField);
however this will require that u.LegacyKeyField is marked as a primary key.
And then I'll give my two cents:
if the Legacy db is using LegacyKeyField, then perhaps the legacy db will be read only. In this case we can create two separate contexts Legacy and Non-legacy and map them accordingly. This can potentially become a bit messy as you'd have to remember which object comes from which context. But then again, nothing stops you from adding the same EF code first object into 2 different contexts
Another solution is to use views with ContactId added for all other legacy tables and map them into one context. This will tax performance for the sake of having cleaner context objects, but this can be counteracted on sql side: indexed views, materialized views, stored procs, etc. So than LEGACY_OBJECT becomes VW_LEGACY OBJECT with CONTACT.ContactId brought over, then:
modelBuilder.Entity<LegacyObject>().ToTable("VW_LEGACY_OBJECT");
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.ContactId);
I personally would go with creating "mapper views" with CustomerId on legacy tables, as it's cleaner from c# layer perspective and you can make those views look like real tables. It is also difficult to suggest a solution without knowing what exactly is the scenario that you have a problem with: querying, loading, saving, etc.

How to pass multiple parameter in DomainService - WCF

Let's say I have a window which should submit 3 model in client side (Silverlight Client Application). My problem is each time I submit the form, data on the server side which I passed them from client are empty.
I've used nested class which contains my models, instead of passing multiple object as parameter, but it didn't work again.
My Personnel Data Transfer Object Code is something like this :
[DataContract]
public class PersonnelDTO : EntityObject
{
[Key]
[DataMember]
public int PersonnelId { get; set; }
[Include]
[DataMember]
[Association("Personnel_ID", "PersonnelId", "Personnel_ID")]
public Personnel Personnel { get; set; }
[Include]
[DataMember]
[Association("Personnel_Info_ID", "PersonnelId", "Personnel_Info_ID")]
public Personnel_Info PersonnelInfo { get; set; }
}
I fill up this model to pass data from client to server (DomainService).
and also my domain service code is :
[Invoke]
public void AddPersonnel(PersonnelDTO personnelDTO)
{
// Model are EMPTY in DTO
ObjectContext.AddToPersonnels(personnelDTO.Personnel);
ObjectContext.AddToPersonnel_Info(personnelDTO.PersonnelInfo);
ObjectContext.SaveChanges();
}
I don't know if there is a way to pass multiple parameter in WCF Service method include Generic List.
Thanks in advance.
First off, you don't want to use Invoke on your service method. You just want an Insert operation. So your method should look like this:
public void InsertPersonnel(PersonnellDTO personnelDTO)
No need for an [Insert] attribute as RIA will automatically generate it by convention of the naming of the method.
The next hurdle you have to deal with is how RIA handles the keys. It uses the keys to determine change-tracking. By DEFAULT - RIA will send down EMPTY objects to the service layer if it thinks the object you are sending down is NOT NEW. It does that to save bandwidth.
You're wrapping your objects in a DTO; RIA doesn't really behave well in that scenario from my experience. What it really expects is a Personnel object with PersonnelInfo object as a child and the PersonnelId as the key. Then you need to setup your associations with IsForeignKey=true so that the keys get updated correctly.
I'll post an example of a complex root aggregate object that I use in a sample application that I'm going to blog about shortly (we're using RIA with POCO and Oracle and it works; but it took some figuring out).
[MetadataType(typeof (TicketMetadata))]
public partial class Ticket
{
internal sealed class TicketMetadata
{
[Key] public int TicketId;
[Required]
public DateTime IncidentDate;
[Required(ErrorMessage = "Missing Customer")]
public int CustomerId;
[Required(ErrorMessage = "Missing Product")]
public int ProductId;
[Include]
[Association("Ticket_Customer", "CustomerId", "CustomerId", IsForeignKey = true)]
public Customer Customer;
[Include]
[Association("Ticket_Product", "ProductId", "ProductId", IsForeignKey = true)]
public Product Product;
[Include]
[Composition]
[Association("Ticket_TicketActions", "TicketId", "TicketId")]
public List<TicketAction> TicketActions;
}
}
I'd recommend looking at the way Association and foreign keys work and rethinking your object structure and possibly moving away from the DTO. Done right, the whole thing works pretty well.

Categories

Resources