I am using example code from a tutorial on Creating a WCF Service and it only half works. Adding to the List works when it's hard-coded and retrieving the List works. However, using a routine to Add or modify the List does not work. Here is the code:
BookData.cs
using System.Runtime.Serialization;
namespace BookServicesV2
{
[DataContract]
public class BookData
{
[DataMember]
public int BookID { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public string Author { get; set; }
[DataMember]
public decimal Price { get; set; }
}
}
BookService.cs:
using System.Collections.Generic;
namespace BookServicesV2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class BookService : IBookService
{
private List<BookData> _books;
private BookService()
{
_books = new List<BookData>();
var book = new BookData { BookID = 1, Author = "Shakespeare", Title = "To Be or Not To Be", Price = 1.49M };
_books.Add(book);
book = new BookData { BookID = 2, Author = "Mark Twain", Title = "Not Dead Yet", Price = 3.50M };
_books.Add(book);
}
#region IBookService Members
public void AddBook(BookData book)
{
if (book != null) _books.Add(book);
}
public void UpdatePrices(decimal increaseAmmount)
{
foreach (var book in _books)
{
book.Price += increaseAmmount;
}
}
public List<BookData> GetBooks()
{
return _books;
}
#endregion
}
}
When I launch the WCF Test Client, GetBooks retrieves the two hard-coded books. When I use AddBook or UpdatePrices and then GetBooks, nothing has changed. I'm basically learning c# and WCF on the fly, so I am completely stumped. Thanks!
Wcf by default create a new instance at each call. Every time you call the service on client, your list will allways have the two hardcoded books.
Check if you creating a new BookService For each Wcf request. You can change the InstanceContextMode to Single in your config or you can do it programatically so that just one instance is called for all requests. You can google out how to set this behaviour
Try changing the service behavior of your BookService.cs.
[ServiceBehavior( ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.Single )]
public class BookService : IBookService{...}
This will change it to a singleton object. In other words, only 1 instance of your service is created.
You can also use InstanceContextMode = InstanceContextMode.PerSession as well. That means an object is created and maintained for each connection. If you close the connection and then re-connect you would have a new instance but you would be dealing with the same instance for each subsequent call you made to the service (assuming you don't close/reconnect).
Related
I currently have a class with around 40 dependency injection. It is a hard to maintain and unit test. I am not sure a good way around.
The code is done for any type of application process that is needed to process (New License, License Renewal, Student Registration, ...), there are around 80 different types applications and what sections are associated with each application type is determined by a database table.
I have a class with all of the possible properties, there are a several more than listed but you should get the idea. Each the properties have their own set of properties that are basic data types or object pointing to other classes.
class Application
{
[JsonProperty(PropertyName = "accounting")]
public Accounting Accounting { get; set; }
[JsonProperty(PropertyName = "application")]
public Application Application { get; set; }
[JsonProperty(PropertyName = "applicationType")]
public ApplicationType ApplicationType { get; set; }
[JsonProperty(PropertyName = "document")]
public List<Attachment> Document { get; set; }
[JsonProperty(PropertyName = "employment")]
public List<Employment> Employment { get; set; }
[JsonProperty(PropertyName = "enrollment")]
public Enrollment Enrollment { get; set; }
[JsonProperty(PropertyName = "individualAddressContact")]
public IndividualAddressContact IndividualAddressContact { get; set; }
[JsonProperty(PropertyName = "instructors")]
public List<Instructor> Instructors { get; set; }
[JsonProperty(PropertyName = "license")]
public License License { get; set; }
[JsonProperty(PropertyName = "licenseRenewal")]
public LicenseRenewal LicenseRenewal { get; set; }
[JsonProperty(PropertyName = "MilitaryService")]
public List<MilitaryService> MilitaryService { get; set; }
[JsonProperty(PropertyName = "paymentDetail")]
public PaymentDetail PaymentDetail { get; set; }
[JsonProperty(PropertyName = "photo")]
public List<Attachment> Photo { get; set; }
[JsonProperty(PropertyName = "portal")]
public Portal Portal { get; set; }
[JsonProperty(PropertyName = "section")]
public List<Section> Section { get; set; }
[JsonProperty(PropertyName = "testingCalendar")]
public TestingCalendar TestingCalendar { get; set; }
[JsonProperty(PropertyName = "testingScore")]
public List<TestingScore> TestingScore { get; set; }
[JsonProperty(PropertyName = "USCitizen")]
public USCitizen USCitizen { get; set; }
}
So this class is sent/received to an Angular 10 front end using Web API's.
When an application is requested the sections and the different properties are initiated and if the application has be started the progress will be reloaded. So it is possible some of properties will be pulled from the database and sent to the Angular app.
So I have something such as
Load(applicationTypeId, applicationId)
{
Get the sections for the application type
For each section in the sections
switch sectionid
case Documents
Load all of the documents required for the application type and get any documents uploaded
case Accounting
Load the payment details, if no payment made calculate the payment
case IndividualAddressContact
Load the person name/address/contact and set a few defaults if the person hasn't started.
.....
next
}
Save()
{
Save the application
switch current section
case Documents
Save all of the documents for the application
case Accounting
Save the payment details for the application
case IndividualAddressContact
Save the person name/address/contact for the application
.....
get the next section
Update the application current section
}
I have put all of the items in the switch into their own classes but in the end I still have 1 point for serialization/deserialization and still end up with to many dependencies injected. Creating a unit test with over 40 dependencies seems hard to maintain and given I won't know which properties will/won't used until an application is requested and loaded from database. I am unsure how to get around the switch, without at some point and time having to have all of the dependencies injected into 1 class.
I would appreciate some ideas of how to get around this.
"I currently have a class with around 40 dependency injection..." - Oh my gosh!
"It is a hard to maintain and unit test..." - I don't doubt that in the least!
SUGGESTED REFACTORING:
Create a class that manages "Applications" (e.g. "ApplicationManager").
Create an abstract class "Application".
One advantage of "abstract class" over "interface" here that you can put "common code" in the abstract base class.
Create a concrete subclass for each "Application" : public class NewLicense : Application, public class LicenseRenewal : Application, etc. etc.
... AND ...
Use DI primarily for those "services" that each concrete class needs.
I'll bet the constructors for your individual concrete classes will only need to inject three or four services ... instead of 40. Who knows - maybe your base class won't need any DI at all.
This is actually a design we're actually using in one of our production systems. It's simple; it's robust; it's flexible. It's working well for us :)
I would recommend using convention over configuration principle, with the Service Locator.
Declare something like IApplicationHandler interface in your program, e.g.
public interface IApplicationQueryHandler
{
Application Populate(Application application);
}
public interface IApplicationSaveHandler
{
Bool Save(Application application);
}
Then, write pieces of your code, with dependencies and such, e.g.
public class AccountingApplicationQueryHandler : IApplicationQueryHandler
{
public Application Populate(Application application) {
//// Load the payment details, if no payment made calculate the payment
return application;
}
}
public class AccountingApplicationSaveHandler : IApplicationSaveHandler
{
public Bool Save(Application application) {
//// Save the payment details for the application
return true; // this just flags for validation
}
}
// repeat for all other properties
Then in your controller, do something like
public class ApplicationController: Controller
{
public readonly IServiceProvider _serviceProvider;
public ApplicationController(IServiceProvider sp) {
_serviceProvider = sp;
}
public Application Load(string applicationTypeId, string applicationId)
{
var application = new Application(); // or get from db or whatever
var queryHandlers = _serviceProvider.GetServices(typeof(IApplicationQueryHandler));
foreach(var handler in queryHandlers) {
application = handler.Populate(application);
}
return application;
}
[HttpPost]
public bool Save(Application application)
{
var result = true;
var saveHandlers = _serviceProvider.GetServices(typeof(IApplicationSaveHandler));
foreach(var handler in queryHandlers) {
result = handler. Save(application);
}
return result;
}
}
You would need to register your handlers, which you can do e.g. like so:
var queryHandlers = Assembly.GetAssembly(typeof(IApplicationQueryHandler)).GetExportedTypes()
.Where(x => x.GetInterfaces().Any(y => y == typeof(IApplicationQueryHandler)));
foreach(queryHandler in queryHandlers) {
services.AddTransient(typeof(IApplicationQueryHandler), queryHandler);
}
// repeat the same for IApplicationSaveHandler
Now finally, you can write unit tests for part of the code like so
[TestClass]
public class AccountingApplicationQueryHandlerTests
{
[TestMethod]
public void TestPopulate()
{
// arrange
var application = new Application();
var handler = new AccountingApplicationQueryHandler(); // inject mocks here
// act
var result = handler.Populate(application);
// Assert
Assert.AreEqual(result. PaymentDetail, "whatever");
}
}
And you can test that your controller calls the right things by mocking IServiceProvider and injecting that with a couple of dummy handlers to confirm they are called correctly.
Following zaitsman's answer you also could create AggregatedApplicationQueryHandler and AggregatedApplicationSaveHandler and pass collection of concrete implementation of IApplicationQueryHandler and IApplicationSaveHandler to its constructor.
Then you don't need foreach loop inside controller(you loop over handlers inside aggregated handler) and always have only one handler passed to controller. Passing its by constructor parameter shouldn't be so much painful.
You also could create facade over some small services and aggregate theirs functions into one bigger facade service.
I want a service I can inject - or in my example get with GetService - that contains settings from my appsettings.json file.
The appsettings.json fragment looks like this:
"ExternalInterfaces": [
{
"Name": "name1",
"BaseUrl": "https://www.baseurl1.svc"
},
{
"Name": "name2",
"BaseUrl": "https://www.baseurl2.svc"
}
]
To do this I have the following interfaces:
using System.Collections.Generic;
namespace Infrastructure.Settings
{
public interface IExternalInterfaceSettingsCollection
{
IReadOnlyCollection<IExternalInterfaceSettings> Settings { get; set; }
}
}
namespace Infrastructure.Settings
{
public interface IExternalInterfaceSettings
{
string Name { get; set; }
string BaseUrl { get; set; }
}
}
and the following corresponding classes:
using System.Collections.Generic;
namespace Infrastructure.Settings
{
public class ExternalInterfaceSettingsCollection : IExternalInterfaceSettingsCollection
{
public IReadOnlyCollection<IExternalInterfaceSettings> Settings { get; set; }
}
}
namespace Infrastructure.Settings
{
public class ExternalInterfaceSettings : IExternalInterfaceSettings
{
const string DefaultName = "newExternalInterface";
const string DefaultBaseUrl = "";
public string Name { get; set; } = DefaultName;
public string BaseUrl { get; set; } = DefaultBaseUrl;
}
}
And in my Startup.cs I have this (definitely gets called with no exceptions):
services.Configure<IExternalInterfaceSettingsCollection>(settings => _configuration.GetSection("ExternalInterfaces").Bind(settings));
and this is then consumed as follows:
var externalInterfaceConfiguration = app.ApplicationServices.GetService<ExternalInterfaceSettingsCollection>();
var Setting1BaseUrl = externalInterfaceConfiguration.Settings
.SingleOrDefault(s => s.Name == "name1")?.BaseUrl;
However, in the last 3 lines, externalInterfaceConfiguration is always null.
I'm clearly missing something, but I can't see what. Any clues?
You've registered IExternalInterfaceSettings, but you're attempting to retrieve ExternalInterfaceSettings. There's no such service in the collection, so the result is null (since you used GetService<T>). If you had used GetRequiredService<T> then an exception would have been thrown telling you as much.
Then, the options pattern is not meant to bind to interfaces. The whole idea is that you're binding to a POCO that represents a specific set of settings. If you want to use an interface, I suppose that's your prerogative, but it's not going to be applicable to the options configuration. In other words, you need the following instead:
services.Configure<ExternalInterfaceSettings>(Configuration.GetSection("ExternalInterfaces"));
(Note, the action overload with Bind is superfluous. You can just pass the config section directly.)
With that, you'll be able to request something like IOptions<ExternalInterfaceSettings>, but you still cannot get ExternalInterfaceSettings directly from the service collection. If you want that functionality, you'll need to add an additional service registration (which can utilize an interface, this time):
services.AddScoped<IExternalInterfaceSettings, ExternalInterfaceSettings>(p =>
p.GetRequiredService<IOptions<ExternalInterfaceSettings>>().Value);
I'm new to visual studio. I create a datacontract about book information. I create a WCF web services parsing a txt file and create a list of instance of that book information.
When I was trying to call this service to get book information and displace on a web form. I found I don't know how to access Datamember of those instance. Can anybody help me?
public interface IService1
{
[OperationContract]
List<Book> GetAllBooks();
[OperationContract]
String SearchBookById();
}
[DataContract]
public class Book
{
[DataMember]
public int Num { get; set; }
[DataMember]
public string ID { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string author { get; set; }
[DataMember]
public int year { get; set; }
[DataMember]
public float price { get; set; }
[DataMember]
public int stock { get; set; }
}
public List<Book> GetAllBooks()
{
var bookList = new List<Book>();
int n = 1;
StreamReader reader = new StreamReader(#"C:\infs 7204\prac3\books(1).txt");
{
string fileLine;
while((fileLine = reader.ReadLine())!= null)
{
string[] bookInfo = fileLine.Split(',');
int year = Int32.Parse(bookInfo[3]);
float price = float.Parse(bookInfo[4].Trim('$'));
int stock = Int32.Parse(bookInfo[5]);
bookList.Add(new Book { Num = n, ID = bookInfo[0], name = bookInfo[1], author=bookInfo[2],
year=year,price=price, stock=stock});
n++;
}
}
return bookList;
}
I need to display those book classes in a table on my webpage. Here is how I want to access the datacontract list. But I got an warrning says "Cannot implicitly convert type'prac3.ServiceReference1.Book[] to 'prac3.Book[]'"
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Service1Client client = new Service1Client();
Book[] list= client.GetAllBooks();
}
}
Also, Is there any segesstion about which conponent in toolbox should I use to display the table?
Try to include the full namespace of Book and see if it works.
Have you added a project reference to the Book class and generated a service reference also? If that's thje case, better remove the project reference and let svcutil generate a common namespace with ädd service reference".
List<prac3.ServiceReference1.Book> books = client.GetAllBooks();
foreach (prac3.ServiceReference1.Book book in books)
{
Console.WriteLine(book.author);
}
Your warning Cannot implicitly convert type'prac3.ServiceReference1.Book[] to 'prac3.Book[] indicates that very likely you have duplicate definition of type Book.
There are two possibilities:
More likely, as Servé Laurijssen has already pointed out, your client project might have a project reference to your service project. Hence you have access to all of the public types defined within it (including Book.) To be sure if your client project has reference to service project: open your client project in Visual Studio, in Solution Explorer expand the References node under your client project, and find if your service project's name appear there.
Or, less likely, you have redefined class Book in your client project.
In both cases, you get two definitions of Book: one prac3.Book (defined in service project) and another prac3.ServiceReference1.Book (generated by service reference in client project, usually in the file Service References\Service Namespace Name\Reference.cs under your client project directory.) In your case, you are trying to set a prac3.ServiceReference1.Book array to a prac3.Book one, hence causing a type mismatch.
For an immediate remedy change the following line in your client code
Book[] list= client.GetAllBooks();
to the following
prac3.ServiceReference1.Book[] list= client.GetAllBooks();
And consider removing the reference to the service project or the duplicate definition. It is very unusual a client has reference to a service assembly that it refers as well.
I have a CibilResponse Class that has properties that are of class type (TUEF class).
I am trying to assign values using : CibilEnquiryEnq.Tuef.Version = "12"; but it throws null reference error. I have already solved this error but through creating an object like : CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF(); and not through constructor.
ICIBIL.cs
[ServiceContract]
public interface ICIBIL
{
[OperationContract]
string InsertCibil(CibilResponse cibilResponse);
[OperationContract]
string TestInsert(string testObj);
[OperationContract]
string GenerateEnquiry(CibilEnquiry testObj);
}
[DataContract]
public class CibilEnquiry
{
[DataMember]
public TUEF Tuef { get; set; }
public CibilEnquiry()
{
this.Tuef = new TUEF();
}
}
[DataContract]
public class TUEF
{
[DataMember]
public string SegmentTag { get; set; }
[DataMember]
public string Version { get; set; }
[DataMember]
public string MemberReferenceNumber { get; set; }
}
Application:(not working)
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilEnquiryEnq.Tuef.Version = "1111"; // null reference error here and Tuef is null
Application:(working)
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF();
CibilEnquiryEnq.Tuef.Version = "1111";//works fine
I don't understand why I have to add CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF(); to make this work. I am already initializing tuef in constructor in my wcf.
I created a sample in a console application (excluded wcf) and it worked fine without having Tuef = new TUEF();, initializing in constructor was enough.
The proxy objects generated by adding a service reference are not the same objects as you are defining in the service contract, they just happen to be created within the same namespace etc under the consuming clients service reference. Basically they are just DTOs that you use to consume the service.
If you want to have strong dependency between the objects then you can not use the service reference and you need to extract the contract to a separate assembly that you can reference.
1) CibilWcfService.Contract - contains the ICIBIL interface + datacontract objects. You need to reference System.ServiceModel, System.ServiceModel.Web and System.Runtime.Serialization for DataContract related attributes.
2) CibilWcfService - This hosts the WCF service and refers the CibilWcfService.Contract assembly.
namespace CibilWcfService
{
using CibilWcfService.Contract;
public class CibilService : ICIBIL
{
// ... Interface implementation
}
}
3) CibilClient - This is your consuming client application, it also refers the CibilWcfService.Contract assembly. You create the channel to the service like this, then the new CibilEnquiry() is using the same constructor as defined in your contract. You need to reference System.ServiceModel for ChannelFactory.
using CibilWcfService.Contract;
var cf = new ChannelFactory<ICIBIL>();
var channel = cf.CreateChannel(new EndpointAddress("http://127.0.01/CibilServiceUri"));
if (channel != null)
{
var CibilEnquiryEnq = new CibilEnquiry();
CibilEnquiryEnq.Tuef.Version = "1111";
channel.GenerateEnquiry(CibilEnquiryEnq);
}
I have been looking around for an answer to this question, but I don't think anyone on SO has faced this exact problem. Really briefly, I am using the reflection provider in C# to create an odata service (like so), and will have two entities: Blocks and Roots.
Blocks are much like files and directories in a filesystem, some can have children blocks and some cannot. Roots are like different user accounts, and a device can only belong to one Root. Here's what I have so far:
Classes.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Data.Services.Common;
using System.Data.Services;
[DataServiceEntity]
public class Block
{
public string Name { set; get; }
public int ID { set; get; }
public int PID { set; get; }
}
public class ParentBlock : Block
{
public List<Block> Children { set; get; }
public int Count { get { return Children.Count(); } }
public ParentBlock()
{
Children = new List<Block>();
}
}
/*other classes that inherit from Block or ParentBlock*/
[DataServiceEntity]
[DataServiceKey("User")]
public class Root
{
public string User { set; get; }
public int ID { set; get; }
public List<Block> Children { set; get; }
public Root()
{
Children = new List<Block>();
}
}
Main.cs
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class RootDataService : DataService<RootService>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.UseVerboseErrors = true;
config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2;
}
}
public class RootService
{
private List<Root> RootList = new List<Root>();
private List<Block> BlockList = new List<Block>();
public RootService()
{
/*initializes RootList and BlockList with test data*/
}
public IQueryable<Root> Roots
{
get
{
return RootList.AsQueryable<Root>();
}
}
public IQueryable<Block> Blocks
{
get
{
return BlockList.AsQueryable<Block>();
}
}
}
This all works pretty well. I run the program, navigate to my browser and I get two collections, Roots and blocks. I can do odata/Roots to get both test accounts, odata/Roots('account1') to get the first account's User and ID. I can do odata/Roots('account1')/Children(5) and get the Block with ID of 5.
But... when I try to do odata/Roots('account1')/Children(5)/Children or odata/Block(5)/Children, I get a 404. This is frustrating since I added validation code to my RootService constructor and the Count property shows that Block(5) has children (2 of them), but I cannot navigate to them? I'm certain there's something pretty obvious that I'm missing, but I haven't found it. More baffling is that both Root and ParentBlock have identical code in regards to making their Children list available to outside classes, but only one of them is navigable. I am grateful for any help!
In your model, the Block type does not have a property called Children. Did you mean to refer to ParentBlock instead of Block in your Blocks entity set and Root.Children property?
Or, if you know that Block(5) happens to be a ParentBlock, you can cast it before accessing its Children property. For example:
odata/Block(5)/YourNamespace.ParentBlock/Children