Dear Programmers,
I have a question that I can't succeed to figure out, it is more related to how should I design the application, and until now I have always overcome this issue by writing a lot of code.
I have to design a silverlight application connected to a database, and I want to use a WCF service between these 2 for many reasons.
If I use the WCF service I will loose the dbcontext structure which is very great when we want to use CollectionViewSource with related fields to bind the xaml controls.
By example, lets say we have simple entities like that, generated from the edmx file: Resistor and ResistorCategories
public partial class ResistorCategories
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorCategories()
{
this.Name = "New";
this.ResistorsSet = new HashSet<ResistorsSet>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorsSet> ResistorsSet { get; set; }
}
public partial class ResistorsSet
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorsSet()
{
this.ResistorStockEntriesSet = new HashSet<ResistorStockEntriesSet>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Resistance { get; set; }
public Nullable<decimal> PowerRating { get; set; }
public Nullable<decimal> Price { get; set; }
public Nullable<decimal> Tolerance { get; set; }
public string SupplierCode { get; set; }
public string ManufacturerCode { get; set; }
public int ResistorCategories_Id { get; set; }
public Nullable<int> Suppliers_Id { get; set; }
public Nullable<int> Manufacturers_Id { get; set; }
public virtual ResistorCategories ResistorCategories { get; set; }
public virtual Suppliers Suppliers { get; set; }
public virtual Manufacturers Manufacturers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorStockEntriesSet> ResistorStockEntriesSet { get; set; }
}
In the xaml, it is very easy to place 2 datagrid and see the list of resistors which belong to a categorie:
In the xaml:
Then I bind each datagrid to its corresponding CollectionViewSource:
ItemsSource="{Binding Source={StaticResource resistorCategoriesViewSource}, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource resistorCategoriesResistorsViewSource}, UpdateSourceTrigger=PropertyChanged}"
And in the code behind file, with lazyloading enabled:
System.Windows.Data.CollectionViewSource resistorCategoriesViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("resistorCategoriesViewSource")));
//
_context.ResistorCategoriesSet.Load();
resistorCategoriesViewSource.Source = _context.ResistorCategoriesSet.Local;
So it can't be easier to display records with their related fields, but in this case the xaml client is querying the database directly and this is what I want to avoid with a WCF service, + adding some standardization and security.
With a WCF, first I have to generate the POCO objects with EF Power Tool, because it can't return IEnumerable by example because of its virtual property (even if I deactivate lazyloading and proxy).
Then I have to query and store in separated collections the POCO objects:
ObservableCollection && ObservableCollection
Then I bind each datagrid to its collection and monitor the selecteditem event to manually change the collection displayed in the datagrid for the resistors!
So I guess there is a solution to use a WCF together with the simplicity of the dbcontext!
Please help me! Thanks in advance
May be, you can implement your own "data provider", based on sample data provider: Entity Framework 5 Sample Provider. But you need to "tunnel" and serialize data to send/receive throught wcf.
Thx for your message, I think yes finally I will have to do sth like that, I am struggling trying to find an easy solution but I guess what I want to do is not possible.
Instead of trying to save time with the binding I should focus on saving time with the generation of data services. wcf data service is a good solution, I also tried codetrigger and I am actually trying to implement it with open ria services..... which seems to be buggy as hell!!
So I don't know honestly, I can do the data part using wcf data services, but I would like the silverlight app to retrieve also the name and roles on the currently authenticated user in the asp.net app in order to customized the UI. So I guess I have to use a 2nd wcf service just for that.
I am maybe dreaming but if possible I would like the silverlight app to receive a notification when a user logged in or out instead of querying the asp.net app all the times.
If I find a solution of course I will post it, because I think it is quite common when someone want to write a silverlight app.
So I have found a solution finally, it was worth searching even if the solution is easy: using WCF Data Services.
It's possible to query the database with all related entities and bind them in the xaml:
As example, consider an entity called ItemsCategory with a list of Items referring to it via a ItemCategory_Id field, and 2 others entities called ItemPrices and ItemImages referring Items via an Item_Id field:
ItemsCategory
Item
ItemPrices
ItemImages
It's not perfect but it looks like that.
So then if you want to place a datagrid for the ItemsCategory and see the related Items in a second datagrid, and the related ItemPrices and ItemImages in a third and fourth datagrid in your xaml you defined it like that:
<navigation:Page.Resources>
<CollectionViewSource x:Key="itemsCategoriesSetViewSource" d:DesignSource="{d:DesignInstance SvcCatalogDatabase:ItemsCategories, CreateList=True}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetViewSource" Source="{Binding Items, Source={StaticResource itemsCategoriesSetViewSource}}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsPricesSetViewSource" Source="{Binding ItemsPrices, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsImagesSetViewSource" Source="{Binding ItemsImages, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>
</navigation:Page.Resources>
To load the entities you first define a collections like that:
public DataServiceCollection<ItemsCategories> ItemsCategoriesTracked { get; set; }
public CollectionViewSource ItemsCategories_CVSrc { get; set; }
And to query the database you just need to query ItemCategories with related entities:
ItemsCategories_CVSrc.Source = null;
ItemsCategoriesTracked.LoadAsync(this.SvcData.ItemsCategoriesSet.Expand("Items").Expand("Items/ItemsPrices,Items/ItemsImages"));
Of course you have defined a handler for the LoadCompleted event of the DataServiceCollection:
ItemsCategoriesTracked.LoadCompleted += new EventHandler<LoadCompletedEventArgs>((sender, e) => DataServiceCollection_LoadCompleted<ItemsCategories>(sender, e, ItemsCategories_CVSrc));
private void DataServiceCollection_LoadCompleted<T>(object sender, LoadCompletedEventArgs e, CollectionViewSource target)
{
if (e.Error == null)
{
if ((sender as DataServiceCollection<T>).Continuation != null)
{
(sender as DataServiceCollection<T>).LoadNextPartialSetAsync();
}
else
{
target.Source = (sender as DataServiceCollection<T>);
}
}
else
{
MessageBox.Show(string.Format("{0}: An error has occured: {1}", typeof(T).Name, e.Error.Message));
}
}
And you've linked the CollectionViewSource to the one defined in the XAML:
ItemsCategories_CVSrc = (CollectionViewSource)this.Resources["itemsCategoriesSetViewSource"];
Voilà! Hope it helps someone!
Related
I have a BindableLayout that uses a List<PricingLevel> as the datasource. One of the columns in the resulting layout uses a converter to derive a calculated result based on a unit price in the view model and the PricingLevel property of the datasource.
public bool IsTaxInclusive { get; set; }
public decimal UnitPrice { get; set; }
public List<PricingLevel> PricingLevels { get; set; }
If the IsTaxInclusive flag is switched I need to update the calculated results (note PropertyChanged events are raised). But because the PricingLevels don't change, I'm not sure how to trigger the update. At the moment I just refresh the PricingLevels list which works but is not ideal. Is there a simple way to force the update?
So the fix was simple. Rather than refresh the entire list, I used a FastObservableCollection and called its RaiseCollectionChanged() method.
public FastObservableCollection<PricingLevel> PricingLevels { get; set; }
...
PricingLevels.RaiseCollectionChanged();
I'm trying to have an MVVM architecture while Models are EF Models too.
In Code :
Model:
public class NotaireDBContext : DbContext
{
public DbSet<Paquet> Paquets { get; set; }
public DbSet<Personne> Personnes { get; set; }
public DbSet<Contrat> Contrats { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite(#"Data Source=db/Notaire.db");
}
public class Paquet
{
public int PaquetId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public string Volume { get; set; }
public string Page { get; set; }
public string Etat { get; set; }
public List<Contrat> Contrats { get; } = new List<Contrat>();
}
public class Personne
{
public int PersonneId { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Nom_pere { get; set; }
public PieceIdentite Piece_identite { get; set; }
public string Num_piece { get; set; }
public string Lieu_naissance { get; set; }
public string Date_naissance { get; set; }
public string Commune { get; set; }
public string Numero_acte { get; set; }
public string Laiv_carte { get; set; } //??????????????
public string Adresse { get; set; }
public string Nationalite { get; set; }
public string Fonction { get; set; }
}
public class Contrat
{
public int ContratId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public List<Personne> Partie_1 { get; set; }
public List<Personne> Partie_2 { get; set; }
public int PaquetId { get; set; }
public Paquet Paquet { get; set; }
}
Views :
PaquetsView.xaml (this is a view of all paquets)
<ScrollViewer Background="#EBEEF5" HorizontalScrollBarVisibility="Disabled"
FlowDirection="RightToLeft">
<ItemsControl x:Name="PaquetsControl" Padding="4">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:PaquetControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--<controls:PaquetControl/>
<controls:PaquetControl/>-->
</ItemsControl>
</ScrollViewer>
I bind it's ItemsSource like this in PaquetsView.xaml.cs :
public partial class PaquetsView : UserControl
{
private NotaireDBContext db = new NotaireDBContext();
public PaquetsView()
{
InitializeComponent();
PaquetsControl.ItemsSource = (from p in db.Paquets select p).ToList();
}
}
The DataTemplate of PaquetView.xaml -> ItemsControl stand in another xaml file (PaquetControl.xaml), which is a UserControl consisting of TextBlocks and Buttons with Menu (and menu item), that show the data held in on Paquet, and should be able to edit/delete said Paquet.
A portion of it :
...
<Button x:Name="MoreButton" Style="{DynamicResource MoreButtonTemplate}"
Grid.Column="2" Click="MoreButtonClicked" Margin="0,-4,-4,0">
<Button.ContextMenu>
<ContextMenu Background="White" FlowDirection="RightToLeft">
<MenuItem Header="Edit" Click="EditMenuItemClick"/>
<MenuItem Header="Archive" Click="ArchiveMenuItemClick"/>
<MenuItem Header="حذف" Click="DeleteMenuItemClick"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
...
<TextBlock Grid.Column="0" Text="{Binding Path=Numero}" FontSize="22" Foreground="Black"/>
...
<TextBlock Grid.Row="1" Text="{Binding Path=Date, StringFormat=yyyy/MM/dd}"
Foreground="Black" FontSize="16"/>
...
<!--other TextBlock binded-->
Now I would like to know how can I make it CRUD with updates of the view.
Summarize, I have an SQLite DB (code first) for data persistence, I can get that data with DBContext, but now I'm seeing that it's better to use MVVM rather than create DBContext each time.
This is a very large topic and I doubt that it will fit the format adopted here.
Therefore, I will briefly outline only the main points.
It is customary to implement WPF using the MVVM pattern.
It is a strict 3-tier architecture: View (WPF) -> ViewModel -> Model.
The Model is responsible for working with "real" data - this is the so-called Business Logic.
View is responsible for creating the GUI.
The peculiarity of WPF is that the UI elements themselves request the data they need through the bindings.
Bindings are created (mostly) to the DataContext.
Therefore, it is necessary to put some special custom type there, which is responsible for the links between the View and the Model.
This type is called ViewModel.
In a typical implementation, the Model basically receives / returns data via methods.
And bindings need properties.
Therefore, one of the main functions of the ViewModel is to provide all the data necessary for the View in its properties.
When an application works with a database, it is customary in Sharpe to implement this in the Repository (Data) pattern.
From the MVVM point of view, such a Repository is part of the Model.
But, for a simpler understanding, to facilitate software maintenance, the Repository is usually implemented in a separate layer.
As a result, we get a four-tier architecture: View -> ViewModel -> Model -> Repository.
According to the rules and principles of OOP, SOLID in a layered architecture, each layer "knows" (has information) only about the underlying layer.
And all non-public information must be encapsulated inside the layer.
EF entities reflect database data, they are mutable, and can have corresponding attributes.
When changing the source, these types can change.
Let's say you, at some point, want to use a set of XML files instead of a database.
And they need entities of a different type.
Therefore, such entities are the internal implementation of the Repository.
And to exchange data with the Model, the Repository must be either Model types or generic DTO types.
At the next level, the ViewModel must also receive data from the Model.
But Model types cannot be used here, since they can be implicitly associated with Business Logic and there is a possibility of creating parasitic connections leading to unpredictable bugs.
At this level (ViewMode-> Model), exclusively DTO types are used for data exchange.
They are preferably immutable.
The next level of exchange of View with ViewModel.
First, the GUI often needs mutable properties. And to auto-update the property view, the type MUST implement INotifyPropertyChanged.
Secondly, to call actions from the GUI, the ViewModel must provide COMMANDS in its properties - this is the ICommand implementation.
For my answers here, I am using the BaseInpc and RelayCommand classes.
Thirdly, in types for View, additional properties are often needed to ensure the logic of the GUI operation: a selected element, an expanded element, instead of the Id of the record, an element reflecting it, etc.
For these reasons, at the ViewModel level, you already need your own types with an implementation other than DTO.
As a result of the above, we can get four different implementations for the type reflecting some record in the database.
The area of use of each type will be in one or two layers.
In order not to get confused in all this, it is better to do each layer in a separate project.
Types used only within the layer are implemented in this project.
The types used by several layers (for example, DTO) are implemented in separate libraries (maybe for simple tasks and one common library).
To maintain abstraction, it is desirable to do all implementations through the preliminary declaration of interfaces.
And transfer information between layers through these interfaces.
I'm working now with converting existing winform app to WPF. I've never coded anything using MVVC pattern before and I have a hard time trying to figure out how to correctly set models, viewmodels and views in my specific case.
App that I want to convert is used for communicate with physical devices using serial port.
Let's first start with existing winform app. Almost any of the logic is in separate .dll file that expose interface which winform consumes. There are really no "code behind" while displaying data because dll already expose everything that is needed. There are only some additional functions in GUI for manipulation of the data, saving current data etc.
The thing is that I really don't understand how to fit this nicely in MVVM. As model as I understand I will need to create some let's say device that will have all the properties that are changeable and readable. And first question, if model needs to be concrete class or it may be some interface (which I already have) or maybe abstract device class (that implements this interface)?
Second question about ViewModels. I understand that ViewModel is somehow used for "glue" model data to view. And from that perspective in ViewModel I can put all the code that as a result format data from device to output it in some nice formats that will be easily databinded to View. But, here is a quirk. I think that this inteface that I'm now using in that .dll file fits nicely to be databinded in gui (or maybe not?).
Another question is about View itself. I understand that View wouldn't be aware of model etc. But could I put in view that code that I already have in winform GUI? (for saving data to csv files, for doing some measurements etc) or maybe I will need to create another model (I have no idea what it may be looks like).
And the last question is where to put all the logic for using serial port, for concrete implementation of all functions etc. I believe that models should be as easy as possible (like in mvc pattern) without any logic and so on (but again, if it should be in that way, maybe model should be only interface?). And ViewModels should have only code for data manipulation to expose data in friendly format to view and convert it back to model. So where rest of the logic should exist? Thank you all in advance for explaining this thing. And in order to put some code to my post, below is interface that I'm using in that .dll file
public interface IScope
{
event EventHandler NewDataInBuffer;
bool Destroy();
bool Connect();
bool Disconnect();
bool StartCapture();
bool StopCapture();
string ScopeName { get; }
IParameter<float> CurrentVoltageLimit { get; }
IParameter<int> DataSamplesPerDiv { get; }
List<IParameter<Config.Timebase>> AvailableTimebaseSettings { get; }
List<IParameter<Config.Coupling>> AvailableCoupleSettings { get; }
List<IParameter<Config.Slope>> AvailableTriggerSlopeSettings { get; }
List<IParameter<Config.VerticalSensitivity>> AvailableSenitivitySettings { get; }
List<IParameter<Config.TriggerMode>> AvailableTriggerModeSettings { get; }
List<IParameter<Config.RecordLength>> AvailableRecordLength { get; }
IParameter<Config.Timebase> TimeBase { get; set; }
IParameter<Config.Coupling> Couple { get; set; }
IParameter<Config.Slope> TriggerSlope { get; set; }
IParameter<Config.VerticalSensitivity> Sensitivity { get; set; }
IParameter<Config.TriggerMode> TriggerMode { get; set; }
IParameter<Config.RecordLength> RecordLength { get; set; }
int TriggerPosition { get; set; }
float TriggerLevel { get; set; }
}
public interface IParameter<T>
{
string ParameterName { get; }
string ParameterValue { get; }
string ParameterUnit { get; }
bool IsReadOnly { get; }
T GetParameter { get; }
}
I'm developing a web app that contains a User entity that is derived from .NET Core's IdentityUser. Lets suppose there is another entity called Comment which has a relation to a user (the user who posted the comment):
public class User : IdentityUser
{
public string SomeExtraField { get; set; }
}
public class Comment
{
//Owner (Creator) of the feedback
public User User { get; set; }
//body of the comment
public string Body { get; set; }
}
Now suppose I have an API endpoint that returns all of the comments in the system. If I query for all comments and include the User relation, when the object gets serialized, everything in the User class is serialized and sent to the client (including the users hashed password, etc). Obviously I don't want this. So I've created a CommentService layer that grabs the Comments from a CommentRepository. From my understanding, the service layer should do the job of mapping the raw Comment object into a Comment DTO, which only contains data that should be sent to the client. I've defined a comment and user DTO like this:
public class UserOutput
{
public string Id { get; set; }
public string SomeExtraField { get; set; }
}
public class CommentOutput
{
public UserOutput User { get; set; }
public string Body { get; set; }
}
Then in my service layer I have something like the following:
//Fetch all comments
var list = await _repository.ListAsync();
//Map comments to DTO
var result = list.Select(x => new CommentOutput
{
Body = x.Body,
User = new UserOutput
{
Id = x.User.Id,
SomeExtraField = x.User.SomeExtraField,
}
});
This all seems to work great. However I can foresee one problem. Lets say I have a large system with Comments, Posts, Likes, Private Messages, etc. I can map them all in a similar fashion above. Then one day I decide to add another field to the UserOutput DTO. Now I have to go through potentially hundreds of mapping code like the sample above to map the new field properly, and whats worse is the compiler wont tell me if I've missed anything. I would like to have a function somewhere that maps a User to a UserOutput but I don't know where it should go.
I've seen some suggestions to put a constructor to the DTO that does the mapping:
public class UserOutput
{
public UserOutput(User user)
{
Id = user.Id;
SomeExtraField = user.SomeExtraField
}
public string Id { get; set; }
public string SomeExtraField { get; set; }
}
but I've seen people against this because it tightly couples the DTO with the Entity. I've also seen suggestions of using Auto Mapper but is also seems an equal amount of people are against it.
Where should I place code that can perform these DTO->entity and entity->DTO mappings so I don't repeat myself all over the place?
Try to check out AutoMapper.
This library will help you to map the Entity Class into the ViewModel.
The way to use it is pretty straightforward.
i'm writing a system to track observation values from sensors (e.g. temperature, wind direction and speed) at different sites. I'm writing it in C# (within VS2015) using a code-first approach. Although i've a reasonable amount of programming experience, I'm relatively new to C# and the code-first approach.
I've defined my classes as below. I've built a REST api to accept observation reading through Post, which has driven my desire to have Sensor keyed by a string rather than an integer - Some sensors have their own unique identifier built in. Otherwise, i'm trying to follow the Microsoft Contoso university example (instructors - courses- enrolments).
What I am trying to achieve is a page for a specific site with a list of the sensors at the site, and their readings. Eventually this page will present the data in graphical form. But for now, i'm just after the raw data.
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Sensor> Sensors { get; set; }
}
public class Sensor
{
[Key]
public string SensorName { get; set; }
public int SensorTypeId { get; set; }
public int SiteId { get; set; }
public ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int Id { get; set; }
public string SensorName { get; set; }
public float ObsValue { get; set; }
public DateTime ObsDateTime { get; set; }
}
and I've created a View Model for the page I'm going to use...
public class SiteDataViewModel
{
public Site Site { get; set; }
public IEnumerable<Sensor> Sensors { get; set;}
public IEnumerable<Observation> Observations { get; set; }
}
and then i try to join up the 3 classes into that View Model in the SiteController.cs...
public actionresult Details()
var viewModel.Site = _context.Sites
.Include(i => i.Sensors.select(c => c.Observations));
i used to get an error about "cannot convert lambda expression to type string", but then I included "using System.Data.Entity;" and the error has changed to two errors... on the 'include', I get "cannot resolve method 'include(lambda expression)'...". And on the 'select' i get "Icollection does not include a definition for select..."
There's probably all sorts of nastiness going on, but if someone could explain where the errors are (and more importantly why they are errors), then I'd be extremely grateful.
Simply you can you use like
viewModel.Site = _context.Sites
.Include("Sensors).Include("Sensors.Observations");
Hope this helps.
The way your ViewModel is setup, you're going to have 3 unrelated sets of data. Sites, sensors, and observations. Sites will have no inherent relation to sensors -- you'll have to manually match them on the foreign key. Realistically, your ViewModel should just be a list of Sites. You want to do
#Model.Sites[0].Sensors[0].Observations[0]
not something convoluted like
var site = #Model.Sites[0]; var sensor = #Model.Sensors.Where(s => SiteId == site.Id).Single(); etc...
Try doing
viewModel.Site = _context.Sites.Include("Sensors.Observations").ToList();
Eager-loading multiple levels of EF Relations can be accomplished in just one line.
One of the errors you reported receiving, by the way, is because you're using 'select' instead of 'Select'
And lastly, be aware that eager-loading like this can produce a huge amount of in-memory data. Consider splitting up your calls for each relation, such that you display a list of Sensors, and clicking, say, a dropdown will call an API that retrieves a list of Sites, etc. This is a bit more streamlined, and it prevents you from getting held up because your page is loading so much information.
Update
I've created a sample application for you that you can browse and look through. Data is populated in the Startup.Configure method, and retrieved in the About.cshtml.cs file and the About.cshtml page.. This produces this page, which is what you're looking for I believe.