In my app, i have a edmx file with some partial classes representing my tables, and a context class wich i have the methods i need i.e GetMessages() and GetMessageById(long idMessage).
In that case, i use the GetMessages() method to fill a grid. Everything normal.
The Message entity class is something like this:
[Table("Message")]
public partial class Message
{
public long IdMessage{get;set;}
public long IdStatus{get;set;}
}
The problem is that i have another table that i have the StatusDescription that i need to get using the IdStatus.
I created another partial class with this property:
public partial class Message
{
private static readonly MessageRepository MessageRepository ;
static Message()
{
MessageRepository = new MessageRepository();
}
public string StatusDescription
{
get { return MessageRepository .GetMessageDescription(this.Cd_SchedulerStatus); }
}
}
And the method in the MessageRepository:
public MessageRepository()
{
_appContext= AppContext.GetContext();
}
public string GetMessageStatusDescription(int statusId)
{
var status = _appContext.Message.FirstOrDefault(id => id.IdStatus.Equals(statusId));
return status != null ? status.StatusDescription : string.Empty;
}
I know that it generates problems and it is not the best approach to deal with it, because im acessing the data inside the entity class, im having the n+1 problem, each time i send a new query to the database.
I would like to know if somebody have this problem and whats the best solution?
I suggest you create a new context for each message description request:
public string GetMessageStatusDescription(int statusId)
{
using (var appContext = AppContext.GetContext())
{
var status = appContext.Message.FirstOrDefault(id => id.IdStatus == statusId);
return status != null ? status.StatusDescription : string.Empty;
}
}
Related
I have a simple scenario using the Entity Framework in C#. I have an Entity Post:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In my PostManager I have these methods:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
res.Validate();
db.SaveChanges();
return res.Id;
}
}
public void UpdatePost(int postId, string newName, string newDescription)
{
using (var db = new DbContext())
{
var data = (from post in db.Posts.AsEnumerable()
where post.Id == postId
select post).FirstOrDefault();
data.Name = newName;
data.Description = newDescription;
data.Validate();
db.SaveChanges();
}
}
The method validate() refers to class:
public static class Validator
{
public static void Validate(this Post post)
{
if ( // some control)
throw new someException();
}
I call the validate method before the savechanges() but after adding the object to the context. What's the best practice to validate data in this simple scenario? It's better validate the arguments instead? What's happen to object post if the validate method throw exception after adding the object to the context?
UPDATE:
I have to throw a custom set of exception depending on data validation error.
I strongly recommend you to (if at all possible) to modify your entity so the setters are private (don't worry, EF can still set them on proxy creation), mark the default constructor as protected (EF can still do lazy loading/proxy creation), and make the only public constructors available check the arguments.
This has several benefits:
You limit the number of places where the state of an entity can be changed, leading to less duplication
You protect your class' invariants. By forcing creation of an entity to go via a constructor, you ensure that it is IMPOSSIBLE for an object of your entity to exist in an invalid or unknown state.
You get higher cohesion. By putting the constraints on data closer to the data itself, it becomes easier to understand and reason about your classes.
You code becomes self-documenting to a higher degree. One never has to wonder "is it OK if I set a negative value on this int property?" if it is impossible to even do it in the first place.
Separation of concerns. Your manager shouldn't know how to validate an entity, this just leads to high coupling. I've seen many managers grow into unmaintainable monsters because they simply do everything. Persisting, loading, validation, error handling, conversion, mapping etc. This is basically the polar opposite of SOLID OOP.
I know it is really popular nowadays to just make all "models" into stupid property bags with getters and setters and only a default constructor because (bad) ORMs have forced us to do this, but this is no longer the case, and there are so many issues with this imo.
Code example:
public class Post
{
protected Post() // this constructor is only for EF proxy creation
{
}
public Post(string name, string description)
{
if (/* validation check, inline or delegate */)
throw new ArgumentException();
Name = name;
Description = description;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
}
Then your PostManager code becomes trivial:
using (var db = new DbContext())
{
var post = new Post(name, description); // possibly try-catch here
db.Posts.Add(post);
db.SaveChanges();
return post.Id;
}
If the creation/validation logic is extremely intricate this pattern lends itself very well for refactoring to a factory taking care of the creation.
I would also note that encapsulating data in entities exposing a minimal state-changing API leads to classes that are several orders of magnitude easier to test in isolation, if you care at all about that sort of thing.
As I mentioned in the comments above, you might want to check out .NET System.ComponentModel.DataAnnotations namespace.
Data Annotations (DA) allows you to specify attributes on properties to describe what values are acceptable. It's important to know that DA is completely independent of databases and ORM APIs such as Entity Framework so classes decorated with DA attributes can be used in any tier of your system whether it be the data tier; WCF; ASP.NET MVC or WPF.
In the example below, I define a Muppet class with a series of properties.
Name is required and has a max length of 50.
Scaryness takes an int but it must be in the range of {0...100}.
Email is decorated with an imaginary custom validator for validating strings that should contain an e-mail.
Example:
public class Muppet
{
[Required]
[StringLength(50)]
public string Name {get; set;}
public Color Color {get; set; }
[Range(0,100)]
public int Scaryness {get; set; }
[MyCustomEmailValidator]
public string Email {get;set; }
}
In my project I have to throw customException when i validate the data. It's possible do it using Data Annotations?
Yes you can. To validate this object at any time of your application (regardless of whether it has reached EF or not) just perform this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
.
.
.
Post post = ... // fill it in
Validator.Validate(post);
public static class Validator
{
public static void Validate(this Post post)
{
// uses the extension method GetValidationErrors defined below
if (post.GetValidationErrors().Any())
{
throw new MyCustomException();
}
}
}
public static class ValidationHelpers
{
public static IEnumerable<ValidationResult> GetValidationErrors(this object obj)
{
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(obj, null, null);
Validator.TryValidateObject(obj, context, validationResults, true);
return validationResults;
}
.
.
.
If you want to get the validation error messages you could use this method:
/// <summary>
/// Gets the validation error messages for column.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public static string GetValidationErrorMessages(this object obj)
{
var error = "";
var errors = obj.GetValidationErrors();
var validationResults = errors as ValidationResult[] ?? errors.ToArray();
if (!validationResults.Any())
{
return error;
}
foreach (var ee in validationResults)
{
foreach (var n in ee.MemberNames)
{
error += ee + "; ";
}
}
return error;
}
The free set of steak knives is that the validation attributes will be detected once the object reaches EF where it will be validated there as well in case you forget or the object is changed since.
I think you should be working with Data Annotation as #Micky says above. Your current approach is validating manually after it has been added.
using System.ComponentModel.DataAnnotations;
// Your class
public class Post
{
[Required]
public int Id { get; set; }
[Required,MaxLength(50)]
public string Name { get; set; }
[Required,MinLength(15),MyCustomCheck] // << Here is your custom validator
public string Description { get; set; }
}
// Your factory methods
public class MyFactory() {
public bool AddPost() {
var post = new Post() { Id = 1, Name = null, Description = "This is my test post"};
try {
using (var db = new DbContext()) {
db.Posts.Add(post);
db.SaveChanges();
return true;
}
} catch(System.Data.Entity.Validation.DbEntityValidationException e) {
Console.WriteLine("Something went wrong....");
} catch(MyCustomException e) {
Console.WriteLine(" a Custom Exception was triggered from a custom data annotation...");
}
return false;
}
}
// The custom attribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MyCustomCheckAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value instanceof string) {
throw new MyCustomException("The custom exception was just triggered....")
} else {
return true;
}
}
}
// Your custom exception
public class MyCustomException : Exception() {}
See also:
DbEntityValidationException class: https://msdn.microsoft.com/en-us/library/system.data.entity.validation.dbentityvalidationexception(v=vs.113).aspx
Default data annotations
http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx
Building your custom data annotations (validators):
https://msdn.microsoft.com/en-us/library/cc668224.aspx
I always use two validations:
client side - using jQuery Unobtrusive Validation in combination with Data Annotations
server side validation - and here it depends on application - validation is performed in controller actions or deeper in business logic. Nice place to do it is to override OnSave method in your context and do it there
Remember that you can write custom Data Annotation attributes which can validate whatever you need.
You can modify the code in this way:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
if(res.Validate())
{
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
db.SaveChanges();
return res.Id;
}
}
else
return -1; //if not success
}
public static bool Validate(this Post post)
{
bool isValid=false;
//validate post and change isValid to true if success
if(isvalid)
return true;
}
else
return false;
}
After adding data to DbContext and before calling SaveChanges() you can call GetValidationErrors() method of DbContext and check its count to quiqckly check if there are any errors. You can further enumerate all of errors and get error details against each of them. I have bundled Error conversion from ICollection to string in GetValidationErrorsString() extension method.
if (db.GetValidationErrors().Count() > 0)
{
var errorString = db.GetValidationErrorsString();
}
public static string GetValidationErrorsString(this DbContext dbContext)
{
var validationErrors = dbContext.GetValidationErrors();
string errorString = string.Empty;
foreach (var error in validationErrors)
{
foreach (var innerError in error.ValidationErrors)
{
errorString += string.Format("Property: {0}, Error: {1}<br/>", innerError.PropertyName, innerError.ErrorMessage);
}
}
return errorString;
}
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 have a string field name consoleServerPort, and I want to trim all the white spaces inside it before saving it. I use to do so inside my controller class, as follow:-
public ActionResult Edit(FirewallJoin fj, FormCollection formValues)
{
fj.ConsoleServerPort = !String.IsNullOrEmpty(fj.ConsoleServerPort) ? fj. ConsoleServerPort.Trim() : "";
But I have to repeat this step on every action method. So I found another way of doing so once at the model level inside an Ivalidatable method as follow:-
public partial class TMSFirewall : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(!String.IsNullOrEmpty(ConsoleServerPort)){
ConsoleServerPort = ConsoleServerPort.Trim();
}
So is my second approach sound valid? or it is better to use the first approach ?
Thanks
Edit
I am using entity Framework and i did the following
i tried the following inside my model class i added the following :-
[MetadataType(typeof(TMSSwitchPort_Validation))]
[Bind(Include = "PortNumber,SwitchID,TechnologyID")]
public partial class TMSSwitchPort //: IValidatableObject
{
}
then inside the MetadataType class i added the following :-
public class TMSSwitchPort_Validation
{
private string consoleServerPort;
[Required]
[StringLength(10)]
[Display(Name="Port Number1111")]
public String PortNumber
{
get { return this.consoleServerPort; }
set { this.consoleServerPort = value.Trim(); }
}
}
but the ConsoleServerPort will not be trimmed? can you advice what might be the problem ?
Thanks
Couldn't you do it on the property setter?
public class FirewallJoin
{
private string _consoleServerPort;
public string ConsoleServerPort
{
get
{
return _consoleServerPort;
}
set
{
_consoleServerPort = value.Trim();
}
}
}
I have a method that searches a database for all the customers with the same company name and then returns a list of their ID numbers and for now I have them being saved to a text file so I can confirm that it worked.
The problem I am having though, is accessing that list of ID numbers and going back and deleting them. In the following code I use a request that will cancel a customer by their ID number. I use a foreach loop that was supposed to get the list of ID's and delete them all but instead it just deletes one and not a different one each time, it deletes the same one each time, rather attempts to, I just get the exception back saying you are trying to delete a customer that was already deleted. Please send any suggestions on what I am doing wrong!
SearchRequest _request;
CancelRequest _request2;
SearchResponse _response;
CancelResponse _response2;
public void ArrangeRequest() {
_request=new CustomerSearchRequest();
_request.Company="Test Inc. ";
}
var customerIds=_response.Customers.Select(c => c.CustID).ToList();
foreach(var custID in customerIds) {
_request2=new CancelRequest();
_request2.CustID=custID;
_request2.Company=_request.Company;
}
public void Response() {
var ws=new RunEngine();
_response=ws.SearchCust(new AppHeader(), _request) as SearchResponse;
_response2=ws.CancelCust(new AppHeader(), _request2) as CancelResponse;
}
You are reusing _request2 field. Instead of storing single cancel request in field, use list of requests:
List<CancelRequest> _cancelRequests;
Then create and add all requests to this list:
var customerIds = _response.Customers.Select(c => c.CustID);
_cancelRequests = customerIds.Select(custID => new CancelRequest {
CustID = custID,
Company = _request.Company
}).ToList();
And process those requests one by one later.
Seems your issue doesn't happen directly with list, because your CancelCust can take one Request at a time. I actually cannot understand where the earliest code fragment you post should be, so I just name it NowhereMethod!
I've also revised for your code, corrected something like _request.Company=_request.Company;. I further deduced all your class hierarchy from the usage according to the code you've post.
As you stated, the deletion is done by CancelRequest, however, as I mentioned above, it can only take one Request at a time, and the Request is inferred that it saves the information only about one customer. Thus, I'm thinking that your issue can simply solve by rewriting the Response method.
You can still think the problem is about to make them a list like other answers shown that, those are correct way to use Linq. Nevertheless, you might need to decide for a correct place to put in some class, and either design a method in a correct way to take the list.
So, it's the code, I deduced and tried to correct; note that I use fields instead of those probably are properties, and put only those are needed.
You might want to take a look of the comments in the code.
partial class Listener /* I named it, to put your code */ {
SearchRequest _request;
CancelRequest _request2;
SearchResponse _response;
CancelResponse _response2;
public void ArrangeRequest() {
_request=new CustomerSearchRequest();
_request.Company="Test Inc. ";
}
void NowhereMethod() {
var customerIds=_response.Customers.Select(c => c.CustID).ToList();
foreach(var custID in customerIds) {
_request2=new CancelRequest();
_request2.CustID=custID;
_request2.Company=_request.Company;
}
}
public void ResponseOriginal() {
var ws=new RunEngine();
_response=ws.SearchCust(new AppHeader(), _request) as SearchResponse;
_response2=ws.CancelCust(new AppHeader(), _request2) as CancelResponse;
}
public void Response() /* tried to correct */ {
var ws=new RunEngine();
_response=ws.SearchCust(new AppHeader(), _request) as SearchResponse;
var customerIds=_response.Customers.Select(c => c.CustID).ToList();
foreach(var custID in customerIds) {
_request2=new CancelRequest();
_request2.CustID=custID;
_request2.Company=_request.Company;
// Seems it should be like this
// but note the assignment might be wrong, it's according to what `CancelCust` returns
// for the correct way to make it a list of Customer is appeared in other answers
_response2=ws.CancelCust(new AppHeader(), _request2) as CancelResponse;
}
}
}
partial class Customer {
public String CustID;
}
partial class Response {
public List<Customer> Customers;
}
partial class Request {
public String Company;
public String CustID;
}
partial class SearchResponse: Response {
}
partial class CancelResponse: Response {
}
partial class SearchRequest: Request {
}
partial class CancelRequest: Request {
}
partial class CustomerSearchRequest: SearchRequest {
}
partial class AppHeader {
}
partial class RunEngine {
public Response SearchCust(AppHeader appHelper, Request request) {
// I don't know what it's like
throw new NotImplementedException();
}
public Response CancelCust(AppHeader appHelper, Request request) {
// I don't know what it's like
throw new NotImplementedException();
}
}
The Request and Customer can either be declared as
partial class Customer {
// Company was not appearing used in the code
public String CustID;
}
partial class Request {
public String Company;
public String CustID;
}
or
partial class Customer {
public String Company;
public String CustID;
}
partial class Request: Customer {
}
will not break the code.
_request appears to be a lone variable, and not a list. It would then only do one record since you're newing it up every single time through the loop and not storing any previous loop values in a list.
Edit: You'd want to do something like this:
var requestList = new List<CancelRequest>();
var customerIds = _response.Customers.Select(c => c.CustID).ToList();
foreach (var custID in customerIds)
{
_request = new CancelRequest();
_request.CustID = custID;
_request.Company = _request.Company;
requestList.Add(_request);
}
i have a Model like so
public class School{
public int Id {get;set;}
public string Name {get;set;}
static private IEnumerable<School> school;
static public IEnumerable<School> Schools(ContextDb context){
if(school != null)
return school;
return(school = context.Schools.ToList());
}
}
Now I have a page that inserts data into the table, using ajax. The problem is when the popup closes, I would regenerate the Academy.Schools again but since the "school" variable is not null (or is cached) it would return the previous data and not the refreshed data (with the newly added record.
With that said, how do i empy that private variable so I would trigger the "return(school = ..);" line in the class?
Thanks!!
Your Model design is very strange (based on the given information it's hard to offer a better one, is is some kind of ActiveRecord pattern?), but with the current design you need a new method which empties the "cache" e.g. set school to null:
public class School{
public int Id {get;set;}
public string Name {get;set;}
static private IEnumerable<School> school;
static public IEnumerable<School> Schools(ContextDb context){
if(school != null)
return school;
return(school = context.Schools.ToList());
}
public static void InvalidateSchools()
{
school = null;
}
}
After calling School.InvalidateSchools the subsequent call to School.Schools will return the new data.