I'm creating an application with employee and employer as a domain objects.
Both of them have a reference to User object where I store password and other account related stuff.
Example:
public class Employee
{
public Guid EmployeeId { get; set; }
public User User { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string About { get; set; }
...
//other properties
}
public class Employer
{
public Guid EmployerId { get; set; }
public User User { get; set; }
public string CompanyName { get; set; }
public string CompanyDescription { get; set; }
public string FoundedYear { get; set; }
...
//other properties
}
public class User
public Guid UserId { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
...
//other properties
}
I'm also using application services where a method represents a single use case.
Let's say I have RegisterEmpolyee method that should save employee to database set his role to "Employee" and send verification email.
This is my code right now. I'm using AspNet.Core.Idenity.UserManager to create user account:
public async Task<EmployeeDto> RegisterEmployee(RegisterEmployeeDto employee)
{
var validateResult = _validatorService.Validate(employee);
if (!validateResult.IsValid)
throw new ServerException
("RegisterEmployeeDto is not valid", validateResult.GetErrors());
await _db.BeginTransactionAsync();
var newUser = new User { UserName = employee.Email, Email = employee.Email };
var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);
if (!userCreationResult.Succeeded)
{
var userCreationErrors = userCreationResult.GetIdentityResultErrors();
throw new ServerException("Error during create User account.", userCreationErrors);
}
await _roleService.AddUserToRoleAsync(newUser.Id, ApplicationRoles.Employee);
var verificationCode = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
newUser.VerificationCode = verificationCode;
await _emailService.SendActivationEmail(newUser.Email, newUser.Id, verificationCode);
var newEmployee = new Employee(employee.Name, employee.Surname, newUser);
await _db.Employees.AddAsync(newEmployee);
await _db.CompleteAsync();
var employeeDto = _mapper.Map<Employee, EmployeeDto>(newEmployee);
_db.CommitTransaction();
return employeeDto;
}
And here are my questions:
Does this code and my approach are fine according to DDD?
Should I extract creation of employee to domain service? Or maybe factory? And if so should I call repository method from there? (I mean service of course)
Let's say should extract creation of employee to domain service. Should I create User internally then?
Like this:
public async Task<Employee> CreateEmployee(RegisterEmployeeDto employee)
{
var newUser = new User { UserName = employee.Email, Email = employee.Email };
var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);
if (!userCreationResult.Succeeded)
{
var userCreationErrors = userCreationResult.GetIdentityResultErrors();
throw new ServerException("Error during create User account.", userCreationErrors);
}
var newEmployee = new Employee(employee.Name, employee.Surname, newUser);
//Should I call repository here?
await _db.Employees.AddAsync(newEmployee);
await _db.CompleteAsync();
return newEmployee;
}
Or maybe pass User as a parameter?
And last question: Where is a right place to checking if user I want to create exist or not? Is Application service appropriate place to do so?
Thank you in advance for answers.
From what I see, User, Employee and Employer are Aggregate roots (AR).
Does this code and my approach are fine according to DDD?
In DDD it's not recommended that an Aggregate have references to other Aggregates other than by ID. Your Employee and Employer AR have such a bad reference so it is not OK. Instead Employee and Employer should contain only a UserId field.
Should I extract creation of employee to domain service? Or maybe factory? And if so should I call repository method from there? (I mean service of course)
From what I can see you have a complex process of creating multiple Aggregates. In DDD you cannot do this atomically, inside a single transaction. Instead, every Aggregate is created/mutated in its own transaction. There is however a tactical pattern of coordinating a long process: Saga/Process manager.
You should define a process of registering an employee as a Saga: RegisterEmployee. This process should have an interface with these methods: create, start, continue. The create method receive all the data it needs to start process. The start method tries to run the individual steps (like createEmployee, createUser etc); if the start method is run again, it should continue from where has stopped, so the Saga should record its status.
The architecture can be made better by making the command on Aggregates as idempotent. In this way, when a Saga restarts it can send again all the commands to the Aggregates; this effectively makes the Saga very simple.
Let's say should extract creation of employee to domain service. Should I create User internally then?
That domain service is in fact the Saga from the previous step. The Saga however should not contain logic that belongs to the Aggregates! Be carefully to not make your domain model anaemic. The Saga should contain only coordinating logic!
And last question: Where is a right place to checking if user I want to create exist or not? Is Application service appropriate place to do so?
What means that an User already exists? There is already an user with that username? If yes, then the simplest solution is to have an unique index on the username column, if possible. If it's not possible (i.e. you have sharding enabled) then you can have another Saga that checks for duplicates and reports to an Admin or something.
Related
Hi there to the good friends of SO!
This is more of a design question so I'll get into a detailed example.
Let me explain the way we're sending emails.
In various parts of the application, we create entries in our Notification table for different kinds of email we might have to send.
For eg: The NotificationQueue table looks like this:
NotificationQueueID OrderID EmailType Notes SentDatetime
1 461196 OrderUpdate SomeNote1 2020-09-01 14:45:13.153
2 461194 OrderCancellation SomeNote2 2020-09-01 14:45:13.153
It's accessed using the property in the DbContext as:
public DbSet<NotificationQueue> NotificationQueues { get; set; }
The different types of email is modeled in an enum:
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
We have a EmailModel class that has a TicketsInNotificationQueue property that has a list of any of the email types we have. For eg: At any given time, it can have list of either UpdatedTickets or CancelledTickets. The email type says what type of tickets are in the TicketsInNotificationQueue property.
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, TicketsInNotificationQueue ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public TicketsInNotificationQueue TicketsInNotificationQueue { get; set; }
}
public class TicketsInNotificationQueue
{
public List<OrderCancellation> CancelledTickets { get; set; }
public List<OrderUpdate> UpdatedTickets { get; set; }
}
public class OrderCancellation : CommonOrderInformation
{
public string SomeOrderId { get; set; }
}
public class OrderUpdate: CommonOrderInformation
{
public string SomeUpdateRelatedProperty { get; set; }
}
public class CommonOrderInformation
{
public int NotificationQueueId { get; set; }
public string ReferenceNumber { get; set; }
}
There's a method that retrieves tickets from Notification table:
public async Task<TicketsInNotificationQueue> GetTicketsfromNotificationQueueAsync(TypeOfEmail emailType)
{
var ticketsInNotificationQueue = new TicketsInNotificationQueue();
using (var dbCon = GetSomeDbContext())
{
var notifications = dbCon.NotificationQueues.Where(x => x.EmailType == emailType.ToString()).ToList();
foreach (var ntf in notifications)
{
if (ntf.EmailType == TypeOfEmail.OrderCancellation.ToString())
{
if (ticketsInNotificationQueue.CancelledTickets == null)
{
ticketsInNotificationQueue.CancelledTickets = new List<OrderCancellation>();
}
ticketsInNotificationQueue.CancelledTickets.Add(new OrderCancellation()
{
NotificationQueueId = ntf.NotificationQueueID,
ReferenceNumber = ntf.OrderID,
SomeOrderId = "Something from a table."
});
}
else if (ntf.EmailType == TypeOfEmail.OrderUpdate.ToString())
{
if (ticketsInNotificationQueue.UpdatedTickets == null)
{
ticketsInNotificationQueue.UpdatedTickets = new List<OrderUpdate>();
}
var notes = dbCon.NotificationQueues.FirstOrDefault(x => x.NotificationQueueID == ntf.NotificationQueueID)?.Notes;
ticketsInNotificationQueue.UpdatedTickets.Add(new OrderUpdate()
{
NotificationQueueId = ntf.NotificationQueueID,
ReferenceNumber = ntf.OrderID,
SomeUpdateRelatedProperty = "Something from a table."
});
}
}
}
return ticketsInNotificationQueue;
}
Now I just take this list, and filter out the notificationIds for the type of tickets that I just received, and work on them down the line. (I need those notificationIds to set the SentDatetime after the notification has been sent).
var ticketsReceived = false;
notificationIds = new List<int>();
if (ticketsInNotificationQueue.CancelledTickets != null && ticketsInNotificationQueue.CancelledTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.CancelledTickets.Select(x => x.NotificationQueueId).ToList();
}
else if (ticketsInNotificationQueue.UpdatedTickets != null && ticketsInNotificationQueue.UpdatedTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.UpdatedTickets.Select(x => x.NotificationQueueId).ToList();
}
if (ticketsReceived)
{
// Proceed with the process of sending the email, and setting the `SentDateTime`
}
The problem I see here is that as the type of emails grows bigger, let's say 10-20, the method to retrieve tickets and filter them out later needs to grow so big that it's going to spin out of control in terms of readability and code manageability which I'm not liking at all. The part where I need to check what emailType is requested in the fetch and what emailType has been received(to get the corresponding notificationIds for SentDateTime update).
So is there some other way to design this workflow (I'm even open to using reflection and such) to make it more manageable and concise?
Any help would be greatly appreciated!
There is significant improvements that you can make to the existing system and the existing code. In the interest of having a more complete answer I'm going to recommend a not-too-expensive system overhaul and then proceed to your exact answer.
A different and industry standard approach
You already have the data structure correct, this is a perfect job for distributed persistent queues, where you don't need to worry about querying the database as much; instead you just enqueue the messages and have a processor that deals with them. Since you're using C# and .net, I strongly encourage you to check out Azure Service Bus. This is effectively a large queue where you can send messages (in your case send email requests) and you can enqueue your messages to different channels in the service bus depending on their type.
You could also look into creating a queue processor / which Azure Functions have a trigger out of the box. Once your email is sent, then you can write to your DB, we've sent this email.
So, the good design looks like
Have distributed persistent queues, channels / enqueue the email requests to them directly.
If you want to process them at a cadence, run your processor using cron - which most industry solutions support.
If you want to process them as they are ending up in the queue, use a trigger.
You can enrich your processor based on your scenario, it looks like it has something to do with orders, so you may need to handle cases like not sending an already queued email after an order in cancelled, etc..
Improving what you have
Due to some circumstances, the solution above might not be available to you - so let's get to it.
See how to refactor switch statements (since you have one with if / else ifs)
https://sourcemaking.com/refactoring/smells/switch-statements
Ways to eliminate switch in code
You could get this through polymorphism, just create a base mail type and override the behaviors in subclasses. This way you can associate the correct queue with the correct email type.
Example:
var results = await getSomeEmails(OrderMail);
// returns a separate processor inherited from the base one, implemented in different ways.
var processor = ProcessorFactory.Create(OrderMail);
await processor.Send(results);
Some more improvements
foreach (var ntf in notifications)
{
if (ntf.EmailType == TypeOfEmail.OrderCancellation.ToString())
You are checking the email type over and over again unnecessarily in this loop, you should look into moving those statements above the for and check through the passed-in parameter, since you already know the type you're querying for.
Thank you for the answer #Mavi Domates.
But this is what I ended up doing:
I modified the EmailModel's TicketsInNotificationQueue property so that instead of having different types of classes for different types of email, we just have one type of common class. This will avoid having us to put those checks for checking what kind of email was requested in the fetch logic and also to retrieve notification Ids down the line (to update SentDateTime after email is sent) as indicated in the original question.
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, IEnumerable<CommonEmailModel> ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public IEnumerable<CommonEmailModel> TicketsInNotificationQueue { get; set; }
}
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
I added a new class called: CommonEmailModel and removed all those different email type classes (classes for OrderCancellation, OrderUpdate etc.).
public class CommonEmailModel
{
// Common to all email types. A lot of email types only need these first 4 properties
public string EmailType { get; set; }
public int NotificationQueueId { get; set; }
public string OrderId { get; set; }
public string Notes { get; set; }
// Cancellation related
public string SomeOrderId { get; set; }
// Update related
public string SomeUpdateRelatedProperty { get; set; }
public static async Task<IEnumerable<CommonEmailModel>> GetEmailBodyRecordsAsync(TypeOfEmail emailType)
{
var emailModels = new List<CommonEmailModel>();
var emailEntries = await EmailNotificationQueue.GetEmailEntriesAsync(emailType);
var relevantOrdIds = emailEntries.Select(x => x.OrderID).Distinct().ToList();
using (var dbCon = GetSomeDbContext())
{
orders = dbCon.Orders.Where(x => relevantOrdIds.Contains(x.OrdNumber)).ToList();
}
foreach (var record in emailEntries)
{
var emailModel = new CommonEmailModel
{
EmailType = emailType,
NotificationQueueId = record.NotificationQueueID,
OrderId = record.OrderID,
Notes = record.Notes,
SomeOrderId = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.SomeOrderIdINeed,
SomeUpdateRelatedProperty = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.UpdateRelatedPropertyINeed
};
emailModels.Add(emailModel);
}
return emailModels;
}
}
I just get the records the following way:
var emailRecords = await CommonEmailModel.GetEmailBodyRecordsAsync(emailType);
And simply pass this to EmailModel constructor as the ticketsInNotificationQueue parameter. No need to do all that extra check of figuring out if records of certain emailType was requested. The views for OrderCancellation and OrderUpdate will use the common properties and their respective relevant properties that are present in the CommonEmailModel class.
if (emailRecords.Any())
{
var emailModel = new EmailModel(emailType, emailRecords);
}
Now all I have to do is pass the notification Ids to a method that marks the SentDateTime column with the current timestamp by simply calling:
if (emailWasSent)
{
await UpdateNotificationSentTimeAsync(emailRecords.Select(t => t.NotificationQueueId));
}
In the future if we keep on adding new emailType (most probably they'll carry the information in those 4 first common properties in CommonEmailModel), we can simply add new properties to the CommonEmailModel to accommodate that and just create a new view. This way I can avoid code repetition and complexity in the fetch and also at the end while updating the SentDateTime.
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 developing an Asp.net mvc project:
There is a List store all the online Pupils
There are few methods:
- Login(Pupil p): if login ok, p will be added to Pupils list
- Logout(Pupil p): if logout ok, p will be removed out of Pupil list
-> 2 methods have a same potential issue is "cannot modify list while modifying" because there are a lot of Pupil login to system and logout at the same time. While adding a pupil the other are being removed from Pupils list -> exception throws
I tried to use lock to lock a list while modifying (insert/remove) but is it a good way to do? Do you have some better idea?
The last method is Claim(Book b)
Administrator put some books in the GUI and all the logged Pupils can see these books. They can claim any book they want. The fastest Pupil claimed is will own that book. So how can we know the fastest claimer? While updating a data row. At the same time there are many books are claimed by many pupils. But only one fastest pupil can own a particular book after claiming successfully
Do you have a solution for this? This solution just like you put a command to buy stocks. The fastest guy will own the stocks
Remember that there are many pupils will do the same thing at the same time. So we have to make sure that system works properly and exactly
Thanks you in advance,
Best regards
The issue I see here is that your design assumes that the application is always on, and that the lists are the absolute truth about who has what book. What happens when the server is reset? What if your library gets big enough to need a second server to the application?
You need to preserve your lists in a database or some other sort of persistent medium. Holding the lists in memory will give you a read/write buffer, but the lists must be populated from the persistence layer of your library database.
using System;
using System.Collections.Concurrent;
namespace TestArea
{
public class Pupil
{
public Guid Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
}
public class Book
{
//Supports having more than one ISBN in the library... We may have more than one To Kill a Mockingbird in our school library
public Guid Id { get; set; }
public string ISBN { get; set; }
}
public class SchoolLibrary
{
private ConcurrentDictionary<Guid, Pupil> Pupils { get; set; }
private ConcurrentDictionary<Guid, Book> Books{ get; set; }
private ConcurrentDictionary<Guid, Guid> CheckOuts { get; set; }
public Pupil Login(string userName, string password)
{
//Call repository to authenticate pupil into library system
//Mocked return assuming password check success
var id = Guid.NewGuid();
return Pupils.GetOrAdd(id, (i) =>
{
//Replace with function to get student info
return new Pupil
{
Id = i,
Name = "Bac Clunky",
UserName = userName
};
});
}
public bool CheckOut(Guid pupilId, Guid bookId)
{
//If book exists
if (Books.ContainsKey(bookId))
{
Guid currentOwner;
//...is not currently checked out by anyone
if (CheckOuts.TryAdd(bookId, pupilId))
{
return true; //book is now checked out
}
if (CheckOuts.TryGetValue(bookId, out currentOwner))
{
return currentOwner == pupilId; //returns true if pupil already has the book, false if another student has it
}
}
return false; //all other cases fail to check out book
}
}
}
I'm working with ASP.NET MVC application which is based on Identity sample available via NuGet. Because of this I already have some classes to work with the database e.g. ApplicationDbContext.
Say, I decided to let users leave requests for the administrator. I've added the Request class to the models:
public class Request
{
public int Id { get; set; }
public string Message { get; set; }
public ApplicationUser User { get; set; }
}
Since the sample uses different managers to work with users, roles, etc, I've decided to create another one called ApplicationRequestManager inside the Identity.config file (though I'm not sure it's a good practice).
public class ApplicationRequestManager : IRequestManager
{
private ApplicationDbContext db = new ApplicationDbContext();
public void Add(Request request)
{
db.Requests.Add(request);
db.SaveChanges();
}
...
}
This class uses the ApplicationDbContext to work with the database and has some methods to create a request, find it and so on.
I've created a method responsible for sending request inside the Manage controller:
public ActionResult SendRequest(IndexViewModel model)
{
Request request = new Request { Message = model.Message, User = UserManager.FindById(User.Identity.GetUserId()) };
requestManager.Add(request);
return View();
}
When this method is invoked, I get the following exception:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
If I understood correctly, the reason of exception is that I use one ApplicationDbContext to get User - via UserManager and I use another ApplicationDbContext to add the request - via RequestManager, so my request is attached to two contexts. As far as I know, such mistake can be avoided by passing the same context to both UserManager and RequestManager. However, UserManager gets its context via the OwinContext together with other managers:
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
How can I make my own manager follow that pattern as well? I've tried to use the CreatePerOwinContext method like
app.CreatePerOwinContext<ApplicationRequestManager>(ApplicationRequestManager.Create);
And I've also tried to implement the Create method following the RoleManager example
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(new RoleStore<ApplicationRole>(context.Get<ApplicationDbContext>()));
}
But I don't have any Store for my requests so I don't know what I should do with the 'new RoleStore' part. How could I solve that problem?
Updated:
I've tried Gert's solution and it worked:
public class Request
{
public int Id { get; set; }
public string Message { get; set; }
[ForeignKey("User")]
public int ApplicationUserId { get; set; }
public ApplicationUser User { get; set; }
}
var userId = User.Identity.GetUserId();
Request request = new Request
{
Message = model.Message,
ApplicationUserId = userId
};
I've also tired another way using HttpConext.Current.GetOwinContext().Get method. I've added the following line to my ApplicationRequestMananger:
public ApplicationRequestManager()
{
this.db = HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>();
}
And it worked fine with the original Request class.
The question is, what advantages and disadvantages does each way have? I've read about foreign keys and I understand the general idea quite well; but I don't really understand what problems can 'HttpContext.Current.GetOwinContext().Get()' cause. Should I use it since it's simpler than adding foreign keys?
The trouble with your design is that each manager has its own context. Seeing this example, I think each manager should call...
db = context.Get<ApplicationDbContext>();
...or receive the request-bounded context in their constructor.
Apart from that, you could make this much simpler by exposing the foreign field to ApplicationUser (ApplicationUserId?) as a primitive property in Request:
public class Request
{
public int Id { get; set; }
public string Message { get; set; }
[ForeignKey("User")]
public int ApplicationUserId { get; set; }
public ApplicationUser User { get; set; }
}
And then create Request like so:
var userId = User.Identity.GetUserId();
Request request = new Request
{
Message = model.Message,
ApplicationUserId = userId
};
This is refered to as foreign key associations, as opposed to independent associations that only have a reference navigation property.
Hello im trying to create a login using wcf but somehow looks like my program dont work as I wanted ;(
public class UserService : IUserService
{
[DataMember]
public string Login { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember]
public int ID { get; set; }
public List<UserInfo> GetUserInformation()
{
QuizDBEntities contex = new QuizDBEntities();
var UserInfo = from a in contex.UserInfoes select a;
return UserInfo.ToList();
}
}
I created
protected void Button1_Click(object sender, EventArgs e)
{
string username = TextBox1.Text;
string password = TextBox2.Text;
UserService vs = new UserService();
List<UserInfo> alfa = new List<UserInfo>();
}
I used few foreach/if loop but every time I do something wrong and my list act like its empty ( I tried grindwiev and did get all data ;( ) Anyone can help me and give hint how can I compare List to login/password ?
A WCF service isn't actually a service until it's hosted somewhere (IIS, self-hosted, etc). Simply adding the attributes [ServiceContract] and [OperationContract] do not magically make it a service.
SOAP Web services like WCF are not directly accessed by the client - the client goes through a proxy to interact with the service. This proxy can be generated automatically by Visual Studio through either Add Service Reference or the command line svcutil.exe. An easy way to do this is to create a new WCF Service Application - this will be hosted in IIS. There are different (and in my opinion better) ways to host the service, but for simplicity and sake of illustration we'll go with this one.
So let's assume you have a WCF service application up and running, and it has the code you posted above. You could then choose Add Service Reference in the VS Solution Explorer to add a service reference to your service. This will generate a proxy for you to use. The name of the proxy is usually UserServiceClient (i.e., Visual Studio adds Client to the end).
To call a method in your service with this proxy, you would do this:
UserServiceClient proxy = new UserServiceClient();
List<UserInfo> users = proxy.GetUserInformation;
This would give you a list of all the users in your database. You would probably want to either markup the UserInfo entity as a DataContract, or create a new class that has the properties in it as a DataContract - your current code doesn't do anything to set the properties it has in it, and services themselves don't do anything with properties (not to mention your code isn't setting any values for them anyway).
Now for your other question - "how can I compare List to login/password"? In a nutshell, you can't. Your List<UserInfo> is a list of UserInfo objects, and you're attempting to compare a string to this list. That won't work.
What you could do, however, is create another method in your service that would accept a username and a password and return that user's information if it is found. It might look something like below, but first let's make a DataContract to hold the UserInfo (basically moving the DataMembers from the service to a separate class:
[DataContract]
public class UserInformation
{
[DataMember]
public string Login { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember]
public int ID { get; set; }
}
public UserInformation GetUser(string userName, string password)
{
UserInformation user = new UserInformation();
using (QuizDBEntities context = new QuizDBEntities())
{
user = (from a in context.UserInfoes
where a.UserName == userName && a.Password == password
select new UserInformation() {
Login = a.UserName,
Password = a.Password,
Type = a.Type,
ID = a.ID}).SingleOrDefault();
}
return user;
}
The UserInformation class contains the DataMembers you originally had in your service. The LINQ query selects the user that has the matching UserName and Password and populates the UserInformation class (property names are conjecture as I don't know what your UserInfo entity looks like). The SingleOrDefault() at the end selects one matching result, or if no match is found returns the default value - which in this case will be null.
You could then use it like this:
UserServiceClient proxy = new UserServiceClient();
UserInformation user = proxy.GetUser("someName", "somePassword");
proxy.Close();
if (user == null)
{
// No match was found, so do something
}
else
{
// Match was found, so proceed with what you were doing
}
All of the above is primarily for illustration purposes, but you should be able to adapt to your program's needs. I would also suggest Googling for some good tutorials on how to create and host a WCF service.