This is one of the class definitions within a DLL I use in my WCF service.
[DataContract]
public class ScenarioXML
{
[DataMember(Order = 1)]
public long? TNRScenario { get; set; }
[DataMember(Order = 2)]
public long? TNRProject { get; set; }
[DataMember(Order = 3)]
public int? Priority { get; set; }
// ...
[DataMember(Order = 19)]
public List<ScenarioAssetXML> ScenarioAssets { get; set; }
[DataMember(Order = 20)]
public List<CalendarXML> Calendars { get; set; }
[DataMember(Order = 21)]
public ScenarioTriggerCollectionXML ScenarioTriggerCollection { get; set; }
}
I'm using DataContract instead of ProtoContract, so I can expose this class to a Silverlight project through a WSDL, and still use Protobuf-net for serialization.
Now, when I use the following code in my WCF service, the original "scenario" and the "restoredModel" are identical.
MemoryStream msTestString = new MemoryStream();
Serializer.Serialize<ScenarioXML>(msTestString, scenario);
string memento = Convert.ToBase64String(msTestString.ToArray());
byte[] byteAfter64 = Convert.FromBase64String(memento);
MemoryStream afterStream = new MemoryStream(byteAfter64);
ScenarioXML restoredModel = Serializer.Deserialize<ScenarioXML>(afterStream);
However, when I use the same code in Silverlight, the TNRScenario value is null.
Similarly, the TNRScenarioAsset property of the objects in the ScenarioAssets list are null.
[DataContract]
public class ScenarioAssetXML
{
[DataMember(Order = 1)]
public long? TNRScenarioAsset { get; set; }
[DataMember(Order = 2)]
public long? TNRScenario { get; set; }
[DataMember(Order = 3)]
public string Asset { get; set; }
[DataMember(Order = 4)]
public string Action { get; set; }
}
When I make the first property a string, it completely vanishes after (de)serialization. When I put a dummy bool as a first property, the bool is there, but the second property, in this case ScenarioAssets, is still null. There's something weird going on here...
Am I doing somethign wrong, or is this a bug?
Edit:
You're right Marc! The orders get messed up in the WSDL-generated code.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="ScenarioXML", Namespace="http://schemas.datacontract.org/2004/07/DataCollectionDLL")]
public partial class ScenarioXML : object, System.ComponentModel.INotifyPropertyChanged {
private System.Nullable<long> TNRScenarioField;
private System.Nullable<long> TNRProjectField;
private System.Nullable<int> PriorityField;
//...
[System.Runtime.Serialization.DataMemberAttribute()]
public System.Nullable<long> TNRScenario {
get {
return this.TNRScenarioField;
}
set {
if ((this.TNRScenarioField.Equals(value) != true)) {
this.TNRScenarioField = value;
this.RaisePropertyChanged("TNRScenario");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute(Order=1)]
public System.Nullable<long> TNRProject {
get {
return this.TNRProjectField;
}
set {
if ((this.TNRProjectField.Equals(value) != true)) {
this.TNRProjectField = value;
this.RaisePropertyChanged("TNRProject");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute(Order=2)]
public System.Nullable<int> Priority {
get {
return this.PriorityField;
}
set {
if ((this.PriorityField.Equals(value) != true)) {
this.PriorityField = value;
this.RaisePropertyChanged("Priority");
}
}
}
//...
However, I'm not sure how to correctly implement that partial class? I created it in the WCF service, but that seems to confuse the compiler. Getting the following errors:
Error 6 'DataCollectionDLL.ScenarioXML' does not contain a definition for 'TNRScenario' and no extension method 'TNRScenario' accepting a first argument of type 'DataCollectionDLL.ScenarioXML' could be found (are you missing a using directive or an assembly reference?)
Error 2 Cannot convert type 'DataCollectionDLL.ScenarioXML [c:\Projects\Flowcontrol 1.7.1.1\flowcontrolFC.Web\Libraries\DataCollectionDLL.dll]' to 'DataCollectionDLL.ScenarioXML [C:\Projects\Flowcontrol 1.7.1.1\flowcontrolFC.Web\DAL\DataCollectionClasses\ScenarioXML.cs(31)]'
Then tried it in the Silverlight project, which compiles fine but that doesn't solve the problem. Results are the same.
The partial class I created:
namespace DataCollectionDLL
{
[ProtoContract]
[ProtoPartialMember(1, "TNRScenario")]
[ProtoPartialMember(2, "TNRProject")]
[ProtoPartialMember(3, "Priority")]
//...
[ProtoPartialMember(21, "ScenarioTriggerCollection")]
partial class ScenarioXML
{
}
}
It sounds like you used WSDL-generated proxies; that can confuse things a little bit, because protobuf-net really really cares what the numbers are, and WSDL can sometimes play fast and loose with those. It would really help if I could see the WSDL-generated proxy classes (in the .designer.cs), but I'm going to assume this is the problem. Fortunately, most code-generators use partial class, so you can add your own partial class in a separate file to add extra information into the same type, in particular: more attributes. For example:
namespace The.Same.Namespace
{
[ProtoContract]
[ProtoPartialMember(1, "TNRScenario")]
[ProtoPartialMember(2, "TNRProject")]
// ...
[ProtoPartialMember(21, "ScenarioTriggerCollection")]
partial class ScenarioXML { }
}
This will get merged by the compiler into the ScenarioXML class, and should allow protobuf-net to use the correct numeric identifiers for each property.
Related
When editing the c# programs I have the top that includes namespaces with the using keyword. Such as using System;.
I have some classes that are redundantly implemented across my WPF application. I would like to create a single reference point to use the methods within the whole application.
for instance when I perform data binding for ComboBoxes I have a class that includes this...
public class ComboBoxItemSource : ViewModelBase
{
public ObservableCollection<Item> Source { get; set; }
public Item Selected { get; set; }
}
I would like to have this in one place and include it in a reference rather than create it in each ViewModel that will need a combobox binding.
I'd recommend to just put the class in a separate file and add it to your project.
Or even simpler:
Right click on your project in the Solution Explorer -> "Add new Item" -> "Class".
Copy/Paste your class into the file
Adjust the namespace
Add the missing usings to the file (especially the using for the "View Model Base")
The class including the namespace in the file will now look similar to this:
namespace MyNamespace
{
public class ComboBoxItemSource : ViewModelBase
{
public ObservableCollection<Item> Source { get; set; }
public Item Selected { get; set; }
}
}
You could do something like this.
Create an IComboBox interface:
public interface IComboBox
{
ObservableCollection<Item> Source { get; set; }
Item Selected { get; set; }
}
then you could create a Class that inherits from your ViewModelBase and implements your IComboBox:
public class ComboBoxVM :ViewModelBase, IComboBox
{
public ObservableCollection<Item> Source
{
get
{
// do stuff
return _source;
}
set { _source = value; }
}
public Item Selected
{
get
{
// do stuff
return _selected;
}
set { _selected = value; }
}
}
then in your ComboBoxItemSource, inherit from ComboBoxVM:
public class ComboBoxItemSource : ComboBoxVM
{
}
Hopefully this helps.
I am working on a WPF application that follows an MVVM pattern. Inspite of moving the validation into services, I am ending up with a fat viewmodels that runs for several lines of code(in my case close to 1000 lines).
I have added the interface for the viewmodel here. I got a few collections exposed as combo and based on the combo selection, I have to perform validation/invoke service/apply filtering to other combos
public interface ISampleViewModel {
ObservableCollection<InstrumentDto> Collection1 { get; set; }
ObservableCollection<TenderViewConfigDetailViewModel> Collection2 { get; set; }
ObservableCollection<TenderViewConfigDetailViewModel> Collection3 { get; set; }
ObservableCollection<TenderViewConfigDetailViewModel> Collection4 { get; set; }
ObservableCollection<TenderViewConfigDetailViewModel> Collection5 { get; set; }
TenderViewConfigDetailViewModel SelectedViewConfigDetail { get; set; }
int SelectedTenderViewIndex { get; set; }
int SelectedInstrumentsViewIndex { get; set; }
SortableCollection<TenderViewToInstrumentViewModel> CurrentInstruments { get; set; }
TenderViewToInstrumentViewModel SelectedInstrumentForTenderView { get; set; }
InstrumentDto SelectedInstrument { get; set; }
bool IsAllInstrumentsFocused { get; set; }
ICommand ApplyChangesCommand { get; }
ICommand AddTenderPanelViewCommand { get; }
ICommand DeleteTenderPanelViewCommand { get; }
ICommand ModifyTenderViewVisiblityCommand { get; }
ICommand AddInstrumentsToPanelViewCommand { get; }
ICommand DeleteInstrumentsFromPanelViewCommand { get; }
ICommand MoveUpTenderListViewCommand { get; }
ICommand MoveDownTenderListViewCommand { get; }
ICommand MoveUpInstrumentsCommand { get; }
ICommand MoveDownInstrumentsCommand { get; }
bool IsValidModel { get; }
void PublishTenderViewConfigChanges(TenderViewConfigDetailViewModel viewModel,EventActionType actionType);
}
The above set of functionality make my viewmodel bulkier. How can I avoid avoid it? I cant think of breaking the functionality into smaller controls as they are dependent? Am I missing something here?
If you have stored in the ViewModel properties that can be isolated in separate classes, it is best to move them to a separate Model. Large number of properties pretty loads ViewModel, for each type of properties you should create your Model. Although on this occasion there is some debate, I believe there is nothing wrong if in the ViewModel will be links to several Models. On this subject you can see this answers:
In MVVM, is every ViewModel coupled to just one Model?
Example of using separate models:
Model
public class MainMenuModel : NotificationObject // Here also implemented INotifyPropertyChanged interface
{
private bool _buttonIsEnabled = true;
public bool ButtonIsEnabled
{
get
{
return _buttonIsEnabled;
}
set
{
_buttonIsEnabled = value;
NotifyPropertyChanged("ButtonIsEnabled");
}
}
}
ViewModel
public class MainMenuViewModel
{
private MainMenuModel _mainMenuModel = null;
public MainMenuModel MainMenuModel
{
get
{
return _mainMenuModel;
}
set
{
_mainMenuModel = value;
}
}
...
public MainMenuViewModel()
{
MainMenuModel = new MainMenuModel();
}
}
View
<Button IsEnabled="{Binding Path=MainMenuModel.ButtonIsEnabled}" ... />
The only thing that can be left on the side of the ViewModel, it Commands and IDataErrorInfo interface implementation, although implementation of IDataErrorInfo can also be moved to the side of the Model.
Also, if the implementation of Command takes a lot of space, you can create separate function / procedure that can be called such Helper and place in suitable class. Next, in Command did not write the whole implementation, it's necessary refer to this method.
For example:
private ICommand _findCommand = null;
public ICommand FindCommand
{
get
{
if (_findCommand == null)
{
_findCommand = new RelayCommand(param => this.Find(), null);
}
return _findCommand;
}
}
private void Find()
{
// Here instead of writing large code,
// moving find logic to separate static class
SomeHelper.FindPerson(MainModel.SearchName);
}
Therefore Command in this case is a wrapper for a call method in ViewModel.
I'm just learning ASP.NET MVC 3, And recently I tried a lot of times to pass arrays/lists/ICollections etc. but couldn't. everytime the list was empty.
For example, the current project:
Model:
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<string> Tags { get; set; }
}
Initializer - Seed:
protected override void Seed(DatabaseContext context)
{
var videos = new List<Video>
{
new Video {
Name = "Video01",
Tags = new List<string> { "tag1", "tag2" },
};
videos.ForEach(s => context.Videos.Add(s));
context.SaveChanges();
base.Seed(context);
}
In the view: I do get the Name property, but the Tags are completely empty.
In the debug I get Tags - Count: 0.
This is not the first time it happens to me, to be honest it happens every single time when I try to pass those kind of stuff. a bit of info about the project:
ASP.NET MVC 3, Entity-Framework:Code First, SqlServerCe.4.0.
Crean an entity Tag
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public int VideoId { get; set; }
public string TagText { get; set; }
}
or store tags to one field separated with comma /semicolon or whatever fits for your solution
By default Entity Framework doesn't load associations of an entity, you need to specify it explicitly:
var videos = context.Videos.Include("Tags");
I have a WCF operation MyGetVersion() that returns a System.Version. When debugging a call to it from a Silverlight service reference, I verified that the service returns the correct System.Version object. In the service reference, the auto-generated method:
public System.Version EndMyGetVersion(System.IAsyncResult result) {
object[] _args = new object[0];
System.Version _result = ((System.Version)(base.EndInvoke("MyGetVersion", _args, result)));
return _result;
}
raises the exception:
Attempt by method 'DynamicClass.ReadVersionFromXml(System.Runtime.Serialization.XmlReaderDelegator, System.Runtime.Serialization.XmlObjectSerializerReadContext, System.Xml.XmlDictionaryString[], System.Xml.XmlDictionaryString[])' to access method 'System.Version..ctor()' failed.
I had to turn on the "break on CLR exception" helper to see this. Otherwise, it is a TargetInvocationException. The System.Version() constructor is public as far as I can tell. What am I doing wrong?
The problem is that the constructor of System.Version is public in the .NET Framework, but it's not in Silverlight (it's internal, according to Reflector). So while the type is serializable in the full framework, it's not in Silverlight, and the Add Service Reference tool should have replaced it with an equivalent type in SL - this is a bug in the tool (I'll report it to the product team, thanks for finding it).
As workarounds, I'd suggest to use a "surrogate" type for Version, and use it in your service contract for data transfer only:
[DataContract]
public class VersionDTO
{
[DataMember]
public int Major { get; set; }
[DataMember]
public int Minor { get; set; }
[DataMember]
public int Build { get; set; }
[DataMember]
public int Revision { get; set; }
public VersionDTO(Version version) {
this.Major = version.Major;
this.Minor = version.Minor;
this.Build = version.Build;
this.Revision = version.Revision;
}
}
[ServiceContract]
public interface ITest
{
[OperationContract]
VersionDTO GetVersion();
}
Another option, given the issue you mentioned in the comment, would be to replace the reference to the Version class in the generated proxy for Silverlight with a class which is equivalent to it. The class below can be used to deserialize a Version object in SL from .NET.
[DataContract(Name = "Version", Namespace = "http://schemas.datacontract.org/2004/07/System")]
public class SLVersion
{
[DataMember(Order = 1, Name = "_Build")]
public int Build { get; set; }
[DataMember(Order = 2, Name = "_Major")]
public int Major { get; set; }
[DataMember(Order = 3, Name = "_Minor")]
public int Minor { get; set; }
[DataMember(Order = 4, Name = "_Revision")]
public int Revision { get; set; }
}
I am having problems databinding to EF code first. I need to be using Eager Loading, but I am running into some issues with databinding. I have the following classes:
public class Context : DbContext
{
DbSet<A> As;
DbSet<B> Bs;
DbSet<C> Cs;
}
public class A
{
public ICollection<B> Bs { get; set; }
public string Name { get; set; }
}
public class B
{
public ICollection<C> Cs { get; set; }
public string Name { get; set; }
}
public class C
{
public string Name { get; set; }
}
I am data binding Context.As to a Treeview, using the below code:
Context.As.Load();
tvItems.ItemsSource = Context.As.Local;
This works as expected, however, it does not automatically load the child properties, Bs, and subsequently, Cs. So, I found that lazy loading can help with this, like so:
Context.As.Load();
tvItems.ItemsSource = Context.As.Include(u=>u.Bs);
From my reading, this should automatically load at least the first level of child properties. However, this will not data bind, as I did not use .Local
.Include() returns IQueryable, which does not support .Local. I can use .ToList(), but this will not automatically update when I add items.
So, how the hell am I supposed to be doing this?
You could try this:
Context.As.Include(a => a.Bs).Load();
tvItems.ItemsSource = Context.As.Local;