I need your help. I wanna implement a database request with different types depending on the users role.
My current query looks like this:
var myVar = session
.Query<TypeA>()
.Where(xyz)
.ToList();
Now I wanna do it like this:
if(UserIsAdmin)
Type T = TypeA;
else
Type T = TypeB;
var myVar= session
.Query<T>()
.Where(xyz)
.ToList();
Notice TypeA an TypeB should be dynamically changed in .Query<T>.
Is there any good way to do this? Do you need more information?
Thanks in advance :-)
EDIT:
The scenario looks as follows:
I have to get different models from the database depending on the users roles. Lets say the user is Admin he can see firstname, lastname, address, if not he can only see firstname, lastname.
I thought to create two different models with these attributes and change the type in the query dynamically.
You need to understand first the consept of seperating layers.
So first of all you will have a DB layer, which in your case(though i don't know you db schema), but it should look something like:
public class UsersDbServices
{
public UserDbEntity GetUserById(int userId)
{
UserDbEntity user = null
using (context..)
{
user = context.Users.Where(u=> u.Id = userId).FirstOfDefault();
}
return user;
}
}
You will call this service from you Logic layer, note that you can use the same method for both normal user and administrator!
Now for the clinet side I would return a DTO(data transfer object) which will be the same for both cases!:
public class UserDTO
{
public int UserId { get;set;}
public string FirstName { get;set;}
public string LastName { get;set;}
public string Address { get;set;}
}
Now the last part is the controller, here you will get the data from the DBLayer(you can go through another layer of logic, but lets say its not a must), and what you will do now in the controller you can poppulate the DTO(and again you can do this all in the logic layer as well) in this way:
public class UsersController
{
UsersDbServices m_UsersDbServices = new UsersDbServices ();
[HttpGet]
public UserDTO GetUserById(userId)
{
UserDTO retVal = null;
UserDbEntity userDbEntity = m_UsersDbServices.GetUserById(userId);
if(userDbEntity == null){
throw...
}
retVal = new UserDTO()
{
FirstName = userDbEntity.FirstName;
...
};
if(!UserIsAdmin) <- kept it as a bool here
{
retVal.Address = null;
}
return retVal; <- convert to json
}
}
Another approach is to use another DTO like AdminUserDTO and RegularUserDTO
Related
I have this method in a Controller:
[Route("api/AppSearch/Search")]
[HttpPost]
[Authorize]
public async Task<ResponseEntity<AppSearchResponse>> Search([FromBody] AppSearchRequest request, string type = "")
{
IEnumerable<AppSearchResponse> data = await SearchServiceV2.Search(request.SearchCriteria, request.AppKeyColumn, request.Filters, request.MaxResults, type);
if (data == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("Unknown type ", type)),
ReasonPhrase = "Unknown type"
};
throw new HttpResponseException(resp);
}
return this.AssembleSuccessResponse<AppSearchResponse>(data);
}
I want to return different data based on if the user is authorized or not. If the user is authorized, I want it to return AppSearchResponse and if not, a class with 3 members of AppSearchResponse.
My initial though is create AppSearchResponseBase that has those 3 fields and then have AppSearchResponse extend it. Then, check if the user is authorized inside the Controller method and serialize the data variable to either the AppSearchResponseBase or AppSearchResponse
e.g.
if(authorized){
return this.AssembleSuccessResponse<AppSearchResponse>(data)
} else {
return this.AssembleSuccessResponse<AppSearchResponseBase>(data)
}
Am I better off just creating a new resource like api/AppSearch/SearchUnauthorized/ ?
I would do something like have a single class of AppSearchResponse that has info like:
public AppSearchResponse{
public string Username {get;set;}
public DateTime Joined {get;set;}
// etc
public ExtendedData ExtendedData {get;set;}
}
And then only populate ExtendedData (which would contain the "secured" fields) if the user has the appropriate access. On the client side, ExtendedData would be null if the client doesn't provide the appropriate credentials or whatever.
my method look like this:
public List<CivarTransporteService.Model.Cliente> getClientes()
{
using (CivarTransporteService.Model.CivarTransporteModelContainer context = new Model.CivarTransporteModelContainer())
{
return context.Cliente.ToList();
}
}
and my cs:
public interface ICatalogsService
{
[OperationContract]
List<CivarTransporteService.Model.Cliente> getClientes();
}
actually getClientes return all the fields of Clientes database but i just need the name of the client
how could i do this? thx
You can use Linq Select extension method to get only the Name column. you need to update your method signature to return a list of string now.
Assuming Name is a property of the Client entity
public List<string> getClientes()
{
using (var context = new Model.CivarTransporteModelContainer())
{
return context.Cliente.Select(x=>x.Name).ToList();
}
}
And the interface signature as well
public interface ICatalogsService
{
[OperationContract]
List<string> getClientes();
}
Or if you do not want to update the existing interface and it's implementation, you can do this at whererever you are calling it.
ICatalogsService catalogService;
catalogService = new SomeConcreteCatalogService(); // not the best to "new" it up.
// But that is not the real question here.
var clientNameList = catalogService.getClients().Select(s=>s.Name).ToList();
Just to expand a little upon #Shyju's answer in case you're interested in more than just 1 field. I did a similar thing when returning database objects over WCF Data Service. I didn't want the entire database model being returned, just a subset of data, so I created an public version of the class:
[DataContract]
public class PublicCliente
{
[DataMember(Order = 1)]
public int Id;
[DataMember(Order = 2)]
public string Name;
public PublicCliente(Cliente c)
{
Id = c.Id;
Name = c.Name;
}
}
Then in the data service method, I did this:
return context.Cliente.Select(
c => new PublicCliente(c)).ToList();
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);
I've got a ViewModel that takes some Model data and slightly alters it.
The way I'm doing it "works" since I just pass the DomainModel to the constructor for the ViewModel, but since I'm using AutoMapper on some of my one-to-one ViewModels, I thought I'd try and learn how to do the mapping across all ViewModels.
Here's an example of a ViewModel that does a little extra.
public class UsersDetailsViewModel
{
public string UserName { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public int ID { get; set; }
public List<OpenID> OpenIds { get; set; }
public string UserAge { get; set; }
public string About { get; set; }
public string Slug { get; set; }
public System.DateTime LastSeen { get; set; }
public string Region { get; set; }
public string MemberSince { get; set; }
public string Reputation { get; set; }
public bool IsUserMatch { get; set; }
private readonly MarkdownDeep.Markdown _markdown;
public UsersDetailsViewModel(Domain.User user)
{
AuthUserData currentuser = AuthenticationHelper.RetrieveAuthUser;
_markdown.NoFollowLinks = true;
_markdown.SafeMode = true;
_markdown.ExtraMode = false;
_markdown.MarkdownInHtml = true;
// We want to ensure that the user has a username, even if they
// haven't set one yet. What this does is check to see if the
// user.UserName field is blank, and if it is, it will set the
// username to "UserNNNN" where NNNN is the user ID number.
_UserName = (user.UserName != null) ? user.UserName : "User" + user.ID.ToString;
// Nothing fancy going on here, we're just re-passing the object from
// the database to the View. No data manipulation!
_Email = user.Email;
_Website = user.WebSite;
_ID = user.ID;
// Get's a list of all of the user's OpenID's
_OpenIds = user.OpenIDs.ToList;
// Converts the users birthdate to an age representation
_UserAge = user.BirthDate.ToAge;
//IE: 29
// Because some people can be real ass holes and try to submit bad
// data (scripts and shitè) we have to modify the "About" content in
// order to sanitize it. At the same time, we transform the Markdown
// into valid HTML. The raw input is stored without sanitization in
// the database. this could mean Javascript injection, etc, so the
// output ALWAYS needs to be sanitized.
// This method below was used in conjunction with MarkDownSharp
// _About = Trim(Utilities.HtmlSanitizer.Sanitize(Markdown.Transform(user.About)))
_About = _markdown.Transform(user.About);
// Removes spaces from Usernames in order to properly display the
// username in the address bar
_Slug = Strings.Replace(user.UserName, " ", "-");
// Returns a boolean result if the current logged in user matches the
// details view of tBhe user in question. This is done so that we can
// show the edit button to logged in users.
_IsUserMatch = (currentuser.ID == user.ID);
// Grabs the users registration data and formats it to a <time> tag
// for use with the timeago jQuery plugin
_MemberSince = user.MemberSince;
// Grabs the users last activity and formats it to a <time> tag
// for use with the timeago jQuery plugin
_LastSeen = user.ActivityLogs.Reverse.FirstOrDefault.ActivityDate;
// Formats the users reputation to a comma Deliminated number
// IE: 19,000 or 123k
_Reputation = user.Reputation.ToShortHandNumber;
// Get the name of the users Default Region.
_Region = user.Region.Name.FirstOrDefault;
}
}
And here's how I currently utilize the above ViewModel
public ActionResult Details(int id)
{
User user = _userService.GetUserByID(id);
if (user != null) {
Domain.ViewModels.UsersDetailsViewModel userviewmodel = new Domain.ViewModels.UsersDetailsViewModel(user);
return View(userviewmodel);
} else {
// Because of RESTful URL's, some people will want to "hunt around"
// for other users by entering numbers into the address. We need to
// gracefully redirect them to a not found page if the user doesn't
// exist.
throw new ResourceNotFoundException();
}
}
How can I use (or should I use) AutoMapper to map my DomainModel to my ViewModel while doing the custom processing you see above?
On automapper where you create the Map you can specify additional processes for specific members of the destination type.
So where your default map would be
Mapper.Map<Domain.User, UsersDetailsViewModel>();
there is a fluent syntax to define the more complicated mappings:
Mapper.Map<Domain.User, UsersDetailsViewModel>()
.ForMember(vm=>vm.UserName, m=>m.MapFrom(u=>(u.UserName != null)
? u.UserName
: "User" + u.ID.ToString()));
Here the ForMember takes two Arguments the first defines the property that you are mapping to. The second provides a means of defining the mapping. For an example I have copped out and shown one of the easy mappings.
If you require a more difficult mapping, (such as your CurrentUser mapping) you can create a class that implements the IResolver interface, incorporate your mapping logic in that new clases and then add that into the mapping.
Mapper.Map<Domain.User, UsersDetailsViewModel>()
.ForMember(vm=>vm.IsUserMatch, m=>m.ResolveUsing<MatchingUserResolver>()));
when Mapper comes to do the mapping it will invoke your custom resolver.
Once you discover the syntax of the .ForMember method everything else kind of slots into place.
Custom mapping can be defined in global.ascx (at startup) by following codes :
AutoMapper.Mapper.CreateMap<Domain.User, UsersDetailsViewModel>()
.ForMember(o => o.Email, b => b.MapFrom(z => z.Email))
.ForMember(o => o.UserName , b => b.MapFrom(user => (user.UserName != null) ? user.UserName : "User" + user.ID.ToString));
you can do some initialization via BeforeMap () method. But you may need to do some changes in your viewmodel.
I think the syntax has slightly changed in 2019 (ASP.NET Core 2.2), this method is now handled with the MapperConfiguration and the static methods are no more available.
But I agree with #KJSR, this post is still really useful :-)
private Mapper UserMapper= new Mapper(new MapperConfiguration(cfg => (cfg.CreateMap<Domain.User, UsersDetailsViewModel>())
.ForMember(x=>x.Email, y=>y.MapFrom(z=>z.Email))
.ForMember(x => x.UserName , y => y.MapFrom(user => (user.UserName != null) ? user.UserName : "User" + user.ID.ToString))));
I've written myself a nice simple little domain model, with an object graph that looks like this:
-- Customer
-- Name : Name
-- Account : CustomerAccount
-- HomeAddress : PostalAddress
-- InvoiceAddress : PostalAddress
-- HomePhoneNumber : TelephoneNumber
-- WorkPhoneNumber : TelephoneNumber
-- MobilePhoneNumber : TelephoneNumber
-- EmailAddress : EmailAddress
This structure is completely at odds with the legacy database I'm having to work with, so I've defined a flat DTO which contains the data for each element in the customer graph - I have views and stored procedures in the database which allow me to interact with the data using this flat structure in both directions, this all works fine & dandy :)
Flattening the domain model into a DTO for insert/update is straightfoward, but what I'm having trouble with is taking a DTO and creating the domain model from it... my first thought was to implement a visitor which would visit each element in the customer graph, and inject values from the DTO as necessary, something a bit like this:
class CustomerVisitor
{
public CustomerVisitor(CustomerDTO data) {...}
private CustomerDTO Data;
public void VisitCustomer(Customer customer)
{
customer.SomeValue = this.Data.SomeValue;
}
public void VisitName(Name name)
{
name.Title = this.Data.NameTitle;
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
}
// ... and so on for HomeAddress, EmailAddress etc...
}
That's the theory and it seems like a sound idea when it's laid out simply like that :)
But for this to work the entire object graph would need to be constructed before the visitor erm, visited, otherwise I'd get NRE's left right and centre.
What I want to be able to do is let the visitor assign objects to the graph as it visits each element, with the goal being to utilize the Special Case pattern for objects where data is missing in the DTO, eg.
public void VisitMobilePhoneNumber(out TelephoneNumber mobileNumber)
{
if (this.Data.MobileNumberValue != null)
{
mobileNumber = new TelephoneNumber
{
Value = this.Data.MobileNumberValue,
// ...
};
}
else
{
// Assign the missing number special case...
mobileNumber = SpecialCases.MissingTelephoneNumber.Instance;
}
}
Which I honestly thought would work, but the C# throws me an error on:
myVisitor.VisitHomePhone(out customer.HomePhoneNumber);
Since you can't pass ref/out parameters in this way :(
So I'm left with visiting independent elements and reconstructing the graph when its done:
Customer customer;
TelephoneNumber homePhone;
EmailAddress email;
// ...
myVisitor.VisitCustomer(out customer);
myVisitor.VisitHomePhone(out homePhone);
myVisitor.VisitEmail(out email);
// ...
customer.HomePhoneNumber = homePhone;
customer.EmailAddress = email;
// ...
At this point I'm aware that I'm quite far away from the Visitor Pattern and am much closer to a Factory, and I'm starting to wonder whether I approached this thing wrong from the start..
Has anyone else run into a problem like this? How did you overcome it? Are there any design patterns which are well suited to this scenario?
Sorry for posting such a looong question, and well done for reading this far :)
EDIT In response to the helpful answers from Florian Greinacher and gjvdkamp, I settled on a relatively simple factory implementation that looks like this:
class CustomerFactory
{
private CustomerDTO Data { get; set; }
public CustomerFactory(CustomerDTO data) { ... }
public Customer CreateCustomer()
{
var customer = new Customer();
customer.BeginInit();
customer.SomeFoo = this.Data.SomeFoo;
customer.SomeBar = this.Data.SomeBar
// other properties...
customer.Name = this.CreateName();
customer.Account = this.CreateAccount();
// other components...
customer.EndInit();
return customer;
}
private Name CreateName()
{
var name = new Name();
name.BeginInit();
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
// ...
name.EndInit();
return name;
}
// Methods for all other components...
}
I then wrote a ModelMediator class to handle interaction between the data layer and the domain model...
class ModelMediator
{
public Customer SelectCustomer(Int32 key)
{
// Use a table gateway to get a customer DTO..
// Use the CustomerFactory to construct the domain model...
}
public void SaveCustomer(Customer c)
{
// Use a customer visitor to scan for changes in the domain model...
// Use a table gateway to persist the data...
}
}
I think you are really over-complicating things here. Just use a factory method and let your domain objects clearly state on which other domain objects they depend.
class Customer
{
private readonly Name name;
private readonly PostalAddress homeAddress;
public Customer(Name name, PostalAddress homeAddress, ...)
{
this.name = name;
this.homeAddress = homeAddress;
...
}
}
class CustomerFactory
{
Customer Create(CustomerDTO customerDTO)
{
return new Customer(new Name(...), new PostalAdress(...));
}
}
If you need to take a dependency from Customer to CustomerDTO pass the DTO as additional argument to the constructor, probably wrapped in an additional abstraction.
This way things will keep clean, testable and easy to understand.
I don't think i would go with a visitor. That would be appropriate if you don't know at design time, what operations you need to perform on it later, so you open up the class to allow for others to write visitors that implement that logic. Or there are so many things that you need to do on it that you don't want to clutter your class with this.
What you want to do here is create an instance of a class from a DTO. Since the structure of the class and the DTO are closely linked (you do your mapping in the DB, I assume you handle all mapping issues on that side and have a DTO format that maps directly to the structure of your customer), you know at design time what you need to. There's no need for much flexibility. (You want to be robust though, that the code can handle changes to the DTO, like new fields, without throwing exceptions)
Basically you want to construct a Customer from a snippet of a DTO. What format do you have, just XML or something else?
I think I would just go for a constructor that accepts the DTO and returns a Customer (example for XML:)
class Customer {
public Customer(XmlNode sourceNode) {
// logic goes here
}
}
The Customer class can 'wrap around' an instance of the DTO and 'become one'. This allows you to very naturally project an instance of your DTO into a customer instance:
var c = new Customer(xCustomerNode)
This handles the high level pattern choice. Do you agree so far?
Here's a stab at the specific issue you mention with trying to pass properties 'by ref'.I do see how DRY and KISS can be at odds there, but I would try not to overthink it. A pretty straight forward solution could fix that.
So for the PostalAddress, it would have it's own constructor too, just like the Customer itself:
public PostalAddress(XmlNode sourceNode){
// here it reads the content into a PostalAddress
}
on the customer:
var adr = new PostalAddress(xAddressNode);
The problem I see here is, where do you put the code that figures out if this if the InvoiceAddress or the HomeAddress? This does not belong in the constructor of the PostalAddress, because there could be other uses for the PostalAddress later, you don't want to hardcode it in the PostalAddress class.
So that task should be handled in the Customer class. This is where he usage of the PostalAddress is determined. It needs to be able to tell from the returned Address what type of address it is. I guess the simplest approach would be to just add a property on PostalAddress that tells us:
public class PostalAddress{
public string AdressUsage{get;set;} // this gets set in the constructor
}
and in the DTO just specify it:
<PostalAddress usage="HomeAddress" city="Amsterdam" street="Dam"/>
Then you can look at it in the Customer class and 'stick it' in the right property:
var adr = new PostalAddress(xAddressNode);
switch(adr.AddressUsage){
case "HomeAddress": this.HomeAddress = adr; break;
case "PostalAddress": this.PostalAddress = adr; break;
default: throw new Exception("Unknown address usage");
}
A simple attribute that tells the Customer what type of address it is would be enough I guess.
How does it sound so far? Code below puts it all together.
class Customer {
public Customer(XmlNode sourceNode) {
// loop over attributes to get the simple stuff out
foreach (XmlAttribute att in sourceNode.Attributes) {
// assign simpel stuff
}
// loop over child nodes and extract info
foreach (XmlNode childNode in sourceNode.ChildNodes) {
switch (childNode.Name) {
case "PostalAddress": // here we find an address, so handle that
var adr = new PostalAddress(childNode);
switch (adr.AddressUsage) { // now find out what address we just got and assign appropriately
case "HomeAddress": this.HomeAddress = adr; break;
case "InvoiceAddress": this.InvoiceAddress = adr; break;
default: throw new Exception("Unknown address usage");
}
break;
// other stuff like phone numbers can be handeled the same way
default: break;
}
}
}
PostalAddress HomeAddress { get; private set; }
PostalAddress InvoiceAddress { get; private set; }
Name Name { get; private set; }
}
class PostalAddress {
public PostalAddress(XmlNode sourceNode) {
foreach (XmlAttribute att in sourceNode.Attributes) {
switch (att.Name) {
case "AddressUsage": this.AddressUsage = att.Value; break;
// other properties go here...
}
}
}
public string AddressUsage { get; set; }
}
class Name {
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
}
and a snippet of XML. You haven't said anything about your DTO format, would work for other formats too.
<Customer>
<PostalAddress addressUsage="HomeAddress" city="Heresville" street="Janestreet" number="5"/>
<PostalAddress addressUsage="InvoiceAddress" city="Theresville" street="Hankstreet" number="10"/>
</Customer>
Regards,
Gert-Jan
For doing conversions between a model class and a DTO, my preference is to do one of four things:
a. use an implicit conversion operator (especially when dealing json-to-dotnet transitions).
public class Car
{
public Color Color {get; set;}
public int NumberOfDoors {get; set;}
}
public class CarJson
{
public string color {get; set;}
public string numberOfDoors { get; set; }
public static implicit operator Car(CarJson json)
{
return new Car
{
Color = (Color) Enum.Parse(typeof(Color), json.color),
NumberOfDoors = Convert.ToInt32(json.numberOfDoors)
};
}
}
and then usage is
Car car = Json.Decode<CarJson>(inputString)
or more simply
var carJson = new CarJson {color = "red", numberOfDoors = "2"};
Car car = carJson;
voila, instant conversion :)
http://msdn.microsoft.com/en-us/library/z5z9kes2.aspx
b. Use linq projection to change the shape of the data
IQueryable<Car> cars = CarRepository.GetCars();
cars.Select( car =>
new
{
numberOfDoors = car.NumberOfDoors.ToString(),
color = car.Color.ToString()
} );
c. Use some combination of the two
d. Define an extension method (that could also be used in the linq projection)
public static class ConversionExtensions
{
public static CarJson ToCarJson(this Car car)
{
return new CarJson {...};
}
}
CarRepository.GetCars().Select(car => car.ToCarJson());
You could take the approch I described here: convert a flat database resultset into hierarchical object collection in C#
The idea behind is to read an object, like Customer and put it into a Dictionary. When reading the data for e.g. CustomerAccount, you can now take the Customer from the Dictionary and add the Customer Account to the customer.
You'll have only one iteration over all data to build your object graph.