I have a solution with 3 projects:
Client
Models
WCF Service
The Models project has POCO classes that reference my database table like below:
[DataContract]
[Table("users")]
public class User
{
public User()
{
}
[DataMember]
[Key, Column("userid", TypeName = "int")]
public Int32 UserId { get; set; }
[DataMember]
[Column("username", TypeName = "varchar")]
public String UserName { get; set; }
The WCF Service Project has a reference to Models in order to return the Model from a Method like below:
public User ValidateUser(string organization, string userName, string password)
{
Model.Poco.User user = new Model.Poco.User();
user.UserId = 1;
user.BoardMember = true;
user.Email = "test#yahoo.com";
user.FirstTimeLogin = false;
user.IsActive = true;
user.Notes = "notes";
user.Password = "xxxxxx";
user.UserName = "user1";
return user;
}
Now, my client has a Service Reference to the WCF Service and the code to call it is like below:
private void button1_Click(object sender, EventArgs e)
{
WCFService.Service1Client client = new WCFService.Service1Client();
var user = client.ValidateUser("test", "test", "test");
}
It's working ok like this, If I navigate "user" variable It has all the object properties from the WCF Service, but what I want to do is to have:
Model.Poco.User user = client.ValidateUser("test", "test", "test");
But if I do this Im getting an error that its not possible to convert from: WCFService.User to Model.Poco.User.
Any clue on how can I convert the serialized object to the native one. Note, that Im using the same object on both client and WCF Service because Im referencing the *Models project on both.*
Appreciate any help in advance!!
When you generate the service reference in the client project, check the option to reuse types in referenced assemblies. Otherwise the tool will generate new classes based on the WSDL as part of the service reference.
Related
Say I have a database in which I am storing user details of this structure:
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
}
I have a data access layer that works with this that contains methods such as GetById() and returns me a User object.
But then say I have an API which needs to return a users details, but not sensitive parts such as the PasswordHash. I can get the User from the database but then I need to strip out certain fields. What is the "correct" way to do this?
I've thought of a few ways to deal with this most of which involve splitting the User class into a BaseClass with non sensitive data and a derived class that contains the properties I would want kept secret, and then converting or mapping the object to the BaseClass before returning it, however this feels clunky and dirty.
It feels like this should be a relatively common scenario, so am I missing an easy way to handle it? I'm working with ASP.Net core and MongoDB specifically, but I guess this is more of a general question.
It seems for my purposes the neatest solution is something like this:
Split the User class into a base class and derived class, and add a constructor to copy the required fields:
public class User
{
public User() { }
public User(UserDetails user)
{
this.UserId = user.UserId;
this.Name = user.Name;
this.Email = user.Email;
}
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserDetails : User
{
public string PasswordHash { get; set; }
}
The data access class would return a UserDetails object which could then be converted before returning:
UserDetails userDetails = _dataAccess.GetUser();
User userToReturn = new User(userDetails);
Could also be done using AutoMapper as Daniel suggested instead of the constructor method. Don't love doing this hence why I asked the question but this seems to be the neatest solution and requires the least duplication.
There are two ways to do this:
Use the same class and only populate the properties that you want to send. The problem with this is that value types will have the default value (int properties will be sent as 0, when that may not be accurate).
Use a different class for the data you want to send to the client. This is basically what Daniel is getting at in the comments - you have a different model that is "viewed" by the client.
The second option is most common. If you're using Linq, you can map the values with Select():
users.Select(u => new UserModel { Name = u.Name, Email = u.Email });
A base type will not work the way you hope. If you cast a derived type to it's parent type and serialize it, it still serializes the properties of the derived type.
Take this for example:
public class UserBase {
public string Name { get; set; }
public string Email { get; set; }
}
public class User : UserBase {
public string UserId { get; set; }
public string PasswordHash { get; set; }
}
var user = new User() {
UserId = "Secret",
PasswordHash = "Secret",
Name = "Me",
Email = "something"
};
var serialized = JsonConvert.SerializeObject((UserBase) user);
Notice that cast while serializing. Even so, the result is:
{
"UserId": "Secret",
"PasswordHash": "Secret",
"Name": "Me",
"Email": "something"
}
It still serialized the properties from the User type even though it was casted to UserBase.
If you want ignore the property just add ignore annotation in you model like this, it will skip the property when model is serializing.
[JsonIgnore]
public string PasswordHash { get; set; }
if you want ignore at runtime(that means dynamically).there is build function avilable in Newtonsoft.Json
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
//FYI ShouldSerialize_PROPERTY_NAME_HERE()
public bool ShouldSerializePasswordHash()
{
// use the condtion when it will be serlized
return (PasswordHash != this);
}
}
It is called "conditional property serialization" and the documentation can be found here. hope this helps
The problem is that you're viewing this wrong. An API, even if it's working directly with a particular database entity, is not dealing with entities. There's a separation of concerns issue at play here. Your API is dealing with a representation of your user entity. The entity class itself is a function of your database. It has stuff on it that only matters to the database, and importantly, stuff on it that does not matter to your API. Trying to have one class that can satisfy multiple different applications is folly, and will only lead to brittle code with nested dependencies.
More to the point, how are you going to interact with this API? Namely, if your API exposes your User entity directly, then any code that consumes this API either must take a dependency on your data layer so it can access User or it must implement its own class representing a User and hope that it matches up with what the API actually wants.
Now imagine the alternative. You create a "common" class library that will be shared between your API and any client. In that library, you define something like UserResource. Your API binds to/from UserResource only, and maps that back and forth to User. Now, you have completely segregated your data layer. Clients only know about UserResource and the only thing that touches your data layer is your API. And, of course, now you can limit what information on User is exposed to clients of your API, simply by how you build UserResource. Better still, if your application needs should change, User can change without spiraling out as an API conflict for each consuming client. You simply fixup your API, and clients go on unawares. If you do need to make a breaking change, you can do something like create a UserResource2 class, along with a new version of your API. You cannot create a User2 without causing a whole new table to be created, which would then spiral out into conflicts in Identity.
Long and short, the right way to go with APIs is to always use a separate DTO class, or even multiple DTO classes. An API should never consume an entity class directly, or you're in for nothing but pain down the line.
I know by creating an user service with entity framework and creating a password hash i can add one or more users to the website! but i'd like to use asp.net identity features even when i want to register batch users (like upload users list with excel or xml file).
In my scenario i want register more than one users on the website by uploading users list in xml,json or excel file. And i want to register all of them in one transaction.
Has anyone idea?
Depends at what level of abstraction you want to work at, but by just looping over the content of the default Register method, you could do something like this:
[HttpPost]
public async Task<IActionResult> RegisterLotsOfPeople(RegisterLotsModel model)
{
var successful = new List<string>();
var failed = new List<string>();
foreach (var toRegister in model.ApplicationUsers)
{
var user = new ApplicationUser {UserName = toRegister.UserName, Email = toRegister.Email};
var result = await _userManager.CreateAsync(user, toRegister.Password);
if (result.Succeeded)
{
successful.Add(toRegister.UserName);
}
else
{
failed.Add(toRegister.UserName);
}
}
return Json(new {SuccessfullyRegistered = successful, FailedToRegister = failed});
}
You'd have to post the data to the end point in JSON format.
The DTO classes are:
public class RegisterLotsModel
{
public List<UserToRegister> ApplicationUsers { get; set; }
}
public class UserToRegister
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
There's lots of other way to do what you're trying to achieve, but this is quick, dirty and would likely work fine. You may need to send the confirmation email in the success part of the method, if email confirmation is required.
Edit - Single Transaction
To insert all the new users in one transaction, you would have to generate their password hashes first, and then insert them into the database.
To get a hash of a given password, you can use the password hasher provided by the UserManager:
foreach (var toRegister in model.ApplicationUsers)
{
var hasher = _userManager.PasswordHasher;
var user = new ApplicationUser {UserName = toRegister.UserName, Email = toRegister.Email};
toRegister.Hashed = hasher.HashPassword(user, toRegister.Password);
}
You could then manually insert these into the database. This would be enough for basic password authentication, but if your implementation uses things like SecurityStamp, you would likely need to implement the appropriate methods when creating the user.
I want to get some understanding about how exactly Automapper is working. I know the basic idea, before I just used so called ViewModels to send the information that the business needs extracted from one or more database tables. Now I'm working on a legacy project where Automapper is used and maybe it offers more than just that but for the given moment I want to undestand (be able) to map my Domain object(s) to my DTO object(s) or vice-versa, I'm not sure which one is the correct way to go since I'm not able to do it.
This is a simple example of a console project I've made to test some basic functionalities of Automapper:
Where the DTO_User.cs class is meant to be used to send a data to the front end. It looks like this:
public class DTO_User
{
public int ID { get; set; }
public string Name { get; set; }
}
And the User.cs is the class which represent the actual domain data :
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
So what I am trying to do. In my Program.cs I have a static method which creates some User obejcts:
public static List<Model.User> SeedUsers()
{
List<Model.User> users = new List<Model.User>()
{
new Model.User { ID = 1, Name = "Ivan", PhoneNumber = "1235436"},
new Model.User { ID = 2, Name = "Petkan", PhoneNumber = "00000000"},
new Model.User { ID = 3, Name = "Dragan", PhoneNumber = "11111111"},
new Model.User { ID = 4, Name = "Stoyan", PhoneNumber = "224545346"}
};
return users;
}
Then In my Main method I try to map this mocked data to my DTO List:
static void Main(string[] args)
{
Mapper.CreateMap<DTO.DTO_User, Model.User>();
//Mock user data as if it's taken from database
List<Model.User> users = new List<Model.User>();
users.AddRange(SeedUsers());//Simulate call to database
//Create List of DTO Users
List<DTO.DTO_User> dtoUsers = new List<DTO.DTO_User>();
//Now map the database users to our DTO Users
foreach (var user in users)
{
DTO.DTO_User u = Mapper.Map<Model.User, DTO.DTO_User>(user);
dtoUsers.Add(u);
}
}
I got the error inside the foreah loop here:
DTO.DTO_User u = Mapper.Map<Model.User, DTO.DTO_User>(user);
Saying that I have some invalid arguments. Obviously I don't really catch the idea how Automapper was meant to implement the actual mapping. The code above is what was looking most natural to me. I know that this is pretty basic so an actual solution won't be too challenging but I would really appreciate if someone explains to me where my logic cracks and what is the idea behind the working code.
Trying adding an additional mapping:
Mapper.CreateMap<Model.User, DTO.DTO_User>();
and changing your Map invocation to the following:
DTO.DTO_User u = Mapper.Map<DTO.DTO_User>(user);
Over the last couple of days I've been researching WCF. I've been reading various discussions and following a number of different walkthroughs, most notably the one linked below.
WCF Getting Started Tutorial
I'm now trying to convert these skills into a different scenario. The file structure is exactly the same as what was stated in the aforementioned walkthrough. There is one WCF Service Library, a Host (Console Application) and a Client (Console Application). The host is setup in the same manor as the walkthrough, the client has been altered to allow user input instead of hardcoded values and the WCF code has been provided below.
After a value is provided the WCF Library will run until the following line var userData = repository.GetById(userId);. When the breakpoint is reached and I step over, an exception is thrown stating that the ConnectionString expected doesn't exist. I've tested putting the connection string in the Client and WCF projects but to no avail. I've also ensured EntityFramework and Service.Configuration DLLs are also added as references.
It's possible I am missing another DLL, App.config setting or I've completely messed up and misinterpretted what I can do with a WCF Service Library so I'm looking for some advice on how to rectify the issue in the previous paragraph or a link which helps me understand the problem
IService.cs Code
[ServiceContract(Namespace = "....")]
public interface IUser
{
[OperationContract]
User GetUser(int userId = 0);
}
[DataContract]
public class User
{
[DataMember]
public int UserId { get; set; }
[DataMember]
public string UserName { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
Service.cs Code
public class UserService : IUser
{
public User GetUser(int userId = 0)
{
User user = null;
using (var context = new Context())
{
var repository = new Repository(context);
var userData = repository.GetById(userId);
if (userData != null)
{
user = new User
{
UserId = userId,
FirstName = userData.CustomerFirstName,
LastName = userData.CustomerSurname,
UserName = userData.CustomerEmail
};
Console.WriteLine("UserId : {0}", userId);
Console.WriteLine("FirstName : {0}", userData.CustomerFirstName);
Console.WriteLine("LastName : {0}", userData.CustomerSurname);
Console.WriteLine("UserName : {0}", userData.CustomerEmail);
}
}
return user;
}
}
Edit:
<add name="CONTEXT"
connectionString="Server=SERVER;Database=DATABASE;uid=DATABASE;pwd=DATABASE;"
providerName="System.Data.SqlClient" />
Your connection string must be set in the App.config of the executing assembly, not in the assembly directly using it. This means that although your service implementation (Service.cs) is located in your WCF Library assembly, the connection string must be located in the App.config of your host project. If you declare an App.config file into your WCF Library, it will simply be ignored.
The client will not access the database, it will consume your service which is who accesses the database. Therefore, there is no need to declare the connectiong string in the client App.config.
I am using Domain Service to fetch data from database from Silverlight Client.
In DomainService1.cs, I have added the following:
[EnableClientAccess()]
public class Product
{
public int productID;
public string productName;
public List<Part> Parts = new List<Part>(); //Part is already present in Model designer
}
In DomainService1 class I added a new method to retrive a collection of the custom class object:
[EnableClientAccess()]
public class DomainService1 : LinqToEntitiesDomainService<HELPERDBNEWEntities1>
{
...
public List<Product> GetProductsList(...)
{
List<Product> resultProducts = new List<Product>();
...
return resultProducts;
}
}
From the silverlight client I am trying to access that method:
DomainService1 ds1 = new DomainService1();
var allproductList = ds1.GetProductsList(...);
ds1.Load<SLProduct>(allproductList).Completed += new EventHandler(Load_Completed); //Not correct usage
However it is not the correct way to call the new method. The reason I added a new class Product in DomainServices.cs is to have an efficient grouping. I cannot achieve the same using the model classes auto-generated by the entity framework.
How call I call the new method from the client?
I believe there is a similar question with an answer here:
Can a DomainService return a single custom type?
Also, here is some discussion about the overall problem of adding custom methods in a Domain Service:
http://forums.silverlight.net/t/159292.aspx/1
While I don't know what you mean by "it is not the correct way to call the new method", or if you're getting any errors, I thought maybe posting some working code might help.
My POCO
public class GraphPointWithMeta
{
[Key]
public Guid PK { get; set; }
public string SeriesName { get; set; }
public string EntityName { get; set; }
public double Amount { get; set; }
public GraphPointWithMeta(string seriesName, string entityName, double amount)
{
PK = Guid.NewGuid();
SeriesName = seriesName;
EntityName = entityName;
Amount = amount;
}
// Default ctor required.
public GraphPointWithMeta()
{
PK = Guid.NewGuid();
}
}
A method in the domain service (EnableClientAccess decorates the class)
public IEnumerable<GraphPointWithMeta> CallingActivityByCommercial()
{
List<GraphPointWithMeta> gps = new List<GraphPointWithMeta>();
// ...
return gps;
}
Called from the Silverlight client like
ctx1.Load(ctx1.CallingActivityByCommercialQuery(), CallingActivityCompleted, null);
client call back method
private void CallingActivityCompleted(LoadOperation<GraphPointWithMeta> lo)
{
// lo.Entities is an IEnumerable<GraphPointWithMeta>
}
I am not sure if your Product class is an actual entity or not. From the way it is defined, it does not appear to be an entity. My answer is assuming it is not an entity. You will need to apply the DataMemberAttribute for your Product properties, and you wouldn't load the product list - load is for Entity Queries (IQueryable on the service side). You would just invoke it like this (client side):
void GetProductList( Action<InvokeOperation<List<Product>>> callback)
{
DomainService ds1 = new DomainService();
ds1.GetProductsList(callback, null);//invoke operation call
}
And the domain service's (server side) method needs the InvokeAttribute and would look like this:
[EnableClientAccess]
public class MyDomainService
{
[Invoke]
public List<Product> GetProductList()
{
var list = new List<Product>();
...
return list;
}
}
And here is how your Product class might be defined (if it is not an entity):
public class Product
{
[DataMember]
public int productID;
[DataMember]
public string productName;
[DataMember]
public List<Part> Parts = new List<Part>(); // you might have some trouble here.
//not sure if any other attributes are needed for Parts,
//since you said this is an entity; also not sure if you
//can even have a list of entities or it needs to be an
//entity collection or what it needs to be. You might
//have to make two separate calls - one to get the products
//and then one to get the parts.
}
Like I said, i am not sure what Product inherits from... Hope this helps.