I am facing a very weird problem while creating a wcf data service with NHibernate 3.0
This is my concrete class which I want to expose as part of data service
public class PayeeType : LookupIdentifierType<int, CamConnect.Entities.DomainEntities.Lookup.PayeeType, PayeeTypeIdentifiers>
{
public virtual String TypeName { get; set; }
}
This is the base class (generic) definition
[IgnoreProperties("Identifier")]
public abstract class LookupIdentifierType<TId, TDto, TIdentifier>
{
public virtual TIdentifier Identifier
{
get
{
return (TIdentifier)Enum.Parse(typeof(TIdentifier), Id.ToString());
}
//set
//{
// String idString = (Convert.ChangeType(value, value.GetTypeCode())).ToString();
// Id = Int32.Parse(idString) as TId;
//}
set
{
//String idString = (Convert.ChangeType(value, value.GetTypeCode())).ToString();
Id = (TId)Convert.ChangeType(value, typeof(TId));
}
}
public virtual String IdentifierText
{
get { return Enum.GetName(typeof(TIdentifier), Identifier); }
set
{
System.Globalization.TextInfo myTI = new System.Globalization.CultureInfo("en-US", false).TextInfo;
TIdentifier temp;
bool success = Enum.TryParse<TIdentifier>(value, true, out temp);
if (!success) Enum.TryParse<TIdentifier>(myTI.ToTitleCase(value).Replace(" ",""), true, out temp);
Identifier = temp;
}
}
public static TReturn GetForIdentifier<TReturn>(TIdentifier identifier) where TReturn : class, ILookupIdentifierType<TIdentifier>
{
Type constructedType = typeof(TReturn);
ILookupIdentifierType<TIdentifier> returnObj = Activator.CreateInstance(constructedType) as ILookupIdentifierType<TIdentifier>;
returnObj.Identifier = identifier;
return returnObj as TReturn;
}
}
Now when I see the service metadata I get very weird names for my base types
<Schema Namespace="Entities.Entities.Lookup"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
<EntityType Name="PayeeType"
BaseType="Entities.Entities.Lookup.LookupIdentifierType
_x0060_3_x005B_System.Int32_x0020_.Entities.DomainEntities.Lookup.
PayeeType_x0020.Entities.Identifiers.PayeeTypeIdentifiers_x005D_" />
This causes the add reference functionality to fail cause (as far as I know) WCF data service doesnt allow underscores in the name and I get 'Name' attribute is invalid error.
Has anybody encountered this before? How can I change the name of the generic base types?
I think you need to specify the data contract name for the base class explicitly when using generics.
See
Naming Generic DataContracts in WCF
Related
I am trying to use a custom attribute on a Entity class generated automatically by the Entity Framework.
The problem is how to add an property attribute on an existing field?
Here the point where I am right now:
// the custom attribute class
public class MyCustomAttribute : Attribute
{
public String Key { get; set; }
}
// Entity Framework class generated automatically
public partial class EntityClass
{
public String Existent { get; set; }
//...
}
// set a metadata class for my entity
[MetadataType(typeof(EntityClassMetaData))]
public partial class EntityClass
{
// if I add a new property to the entity, it works. This attribute will be read
[MyCustomAttribute(Key = "KeyOne" )]
public int newProp { get; set; }
}
public class EntityClassMetaData
{
// adding the custom attribute to the existing property
[MyCustomAttribute(Key = "keyMeta") ]
public String Existent { get; set; }
}
Running this test:
[TestMethod]
public void test1()
{
foreach (var prop in typeof(EntityClass).GetProperties())
{
var att = prop.GetCustomAttribute<MyCustomAttribute>();
if (att != null)
{
Console.WriteLine($"Found {att.Key}");
}
}
}
will produce:
Found KeyOne
Or the Metadata class store the attribute in a different way or only works for data annotations.
I am stuck here, how can I set and read custom attributes of the generated class without having to edit the generated file?
I came across this same problem today. I figured EF magic would do the trick and map the attribute across to each model property. Turns out it does, but only for EF data annotations and I couldn't find an answered solution to pull out custom attributes so made this function. Hope it helps dude.
private object[] GetMetadataCustomAttributes(Type T, string propName)
{
if (Attribute.IsDefined(T, typeof(MetadataTypeAttribute)))
{
var metadataClassType =
(T.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault() as
MetadataTypeAttribute).MetadataClassType;
var metaDataClassProperty = metadataClassType.GetProperty(propName);
if (metaDataClassProperty != null)
{
return metaDataClassProperty.GetCustomAttributes(true);
}
}
return null;
}
I believe if you want to set an attribute in the metadata class, you have to use this syntax:
public class EntityClassMetaData
{
// adding the custom attribute to the existing property
[MyCustomAttribute(Key = "keyMeta") ]
public String Existent;
}
You must not have { get; set; } on your pre-existing property - just the property with the correct name and datatype.
I'm trying to serialize enum values which potentially do not exist yet.
I have an existing project which has several enums in our datacontract for simplicity reason I display one like so:
public partial class TestDTO : ITestDTO
{
public DeleteMe DeleteMeEnum { get; set; }
}
[DataContract]
public enum DeleteMe
{
[EnumMember]
Deleted = 0,
}
Our application has a hidden internal wcf layer which our public web api accesses. A sample Service contract looks like so:
[ServiceContract]
public interface ITestService
{
[OperationContract]
TestDTO GetTestDTO();
}
public class TestService : ITestService
{
public TestDTO GetTestDTO()
{
return new TestDTO() { DeleteMeEnum = (DeleteMe)2 };
}
}
When I call this method from WebApi obviously I get the classic error:
Enum value '2' is invalid for type 'DeleteMe' and cannot be serialized. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute.
I can't go and change all of the enums now because we have a massive project, and replacing them would be too much, Also replacing all of our Service Contracts with a new Attibute would be too much.
Does anyone know of a way I can fix this globally, such as replacing the default XMLSerializer with a custom XMLSerializer?
There isn't a nice way to handle this once your application is released. However if you plan for the situation ahead of time, it can be handled.
So for the example above, you can do this:
public partial class TestDTO : ITestDTO
{
[DataMember(Name = "DeleteMeEnum")]
private string DeleteMeEnumString
{
get { return DeleteMeEnum.ToString(); }
set {
DeleteMe _enum;
if (!Enum.TryParse(value, out _enum))
{
_enum = <default value>;
}
DeleteMeEnum = _enum;
}
}
[IgnoreDataMember]
public DeleteMe DeleteMeEnum { get; set; }
}
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;
}
I have standard XML data coming in that represents a purchase order from a customer. Each customer will populate the XML data differently so I need a separate method to process the order based on their specifications. My goal is to make this scalable so I used an interface because I would like to be able to create additional classes as new customers are added.
How do I select a different Map class based on the customer?
public class XmlPurchaseOrder
{
public DateTime Created { get; set; }
public string CustomerId { get; set; }
public string PurchaseOrderId { get; set; }
public string MapName { get; set; }
//...
}
public interface IXmlMapper
{
CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po);
}
public class CustomerOrder
{
public int Id { get; set; }
public string CustomerId { get; set; }
public string CustomerPoId { get; set; }
public DateTime OrderDate { get; set; }
}
//Maps by customer
public class McClownMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "McD123",
CustomerPoId = po.PurchaseOrderId,
OrderDate = DateTime.Today
};
}
}
public class BkMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "BxK331",
CustomerPoId = string.Format("BxK{0}", po.PurchaseOrderId),
OrderDate = DateTime.Today.AddDays(-1)
};
}
}
public class TacoWorldMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "TW-33",
CustomerPoId = string.Format("{0}-{1}",po.PurchaseOrderId, DateTime.Now.Ticks),
OrderDate = po.Created
};
}
}
class Program
{
private static void Main(string[] args)
{
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//How do I create the associated class by the MapName specified.
IXmlMapper t = Activator.CreateInstance(Type.GetType(po.MapName));
var customerOrder = t.MapToCustomerOrder(po);
//...
}
}
Thanks
Perhaps you could split the workload, so that your Deserializer decorates the XmlPurchaseOrder with a PurchaseOrderType (enum) based on the characteristics that determines the purchase order type. If this is determined by the XML structure itself, like via a tag or an attribute, this is a simple task - otherwise subclass the XmlPurchaseOrder and introduce a virtual method that "calculates" the type.
The other part of the job is to instantiate the concrete PurchaseOrder - this can be simplified using a Factory with one Create method for each kind of purchase order, or more brute force with a big switch on the PurchaseOrderType enum.
A very simple way would be to add a config setting for each customer that maps to the type used to process their order.
<appSettings>
<add key="Customer1" value="MyApp.Logic.Customer1Processor" />
<add key="Customer2" value="MyApp.Logic.Customer2Processor" />
//etc...
</appSettings>
then use Activator.CreateInstance like you have currently.
This makes me think of the Provider Model available through .Net. I am currently using it to instantiate different API Providers based on their Provider Type.
You can set up a near infinite number of different classes that inherit from ProviderBase and add whatever methods you will need to this class. Then, you create each .dll to perform whatever functionality you need and since they have all inherited from some similar base class, you can put the primary method to begin processing the functionality in there.
Base class:
namespace ProviderManager
{
abstract public class SendProviderBase : ProviderBase
{
abstract public void Process(whatever args you need);
}
}
Helper class used to instantiate different Providers
namespace ProviderManger
{
public class ProviderManger
{
private ConfigHandler sendConfig;
public ProviderManger()
{
sendConfig = ConfigurationManger.GetSection("sendProvider") as ConfigHandler;
}
public SendProviderBase GetSendProviderBase(string MapName)
{
try
{
ProviderSettings settings = sendConfig.Providers[MapName];
return (SendProviderBase)ProvidersHelper.InstantiateProvider(settings, typeof(SendProviderBase));
}
//appropriate catch block and whatever else
}}
ConfigHandler code
namespace ProviderManger
{
class ConfigHandler : ConfigurationSection
{
[ConfigurationProperty("providers")}
public ProviderSettingsCollection Providers
{
get
{ return base["providers"] as ProviderSettingsCollection; }
}}}
Usage in Main for you
providerManager = new ProviderManager();
SendProviderManger provider = providerManager.GetSendProviderBase(MapName);
provider.Process(whatever args...);
Obviously you could rename SendProviderBase to something more related to what you're doing but I kept that name since it was consistent through my code here. The only other thing you'll need is a declaration of the .config section used to store MapNames that map to the .dll that is related to it. Since my application is a web service we have a web.config with the following sections:
Custom Section declaration:
<configSections>
<section name="sendProvider" type="KC.ProviderManager.ConfigHandler, ProviderManager"/>
</configSections>
And the Send Provider section:
<sendProviders>
<providers>
<add name="MapNameX" type="namespace.classname, assemblyname">
So basically what this does is you feed providerManger.GetSendProviderBase(MapNameX) the name in the web.config and it returns to you (assuming everything else is built correctly) the class found in that assembly. Then you can call the method found on the base class to begin processing (provider.Process()).
The other necessary References are as follows
System.Reflection;
System.Configuration;
System.Configuration.Provider;
System.Web.Configuration;
This is highly scalable as you can add as many providers as you want as long as they inherit correctly
Or, for a more simplified but still quite scalable solution similar to this check out this link
I did some further research and what I needed was a Factory. This is my interpretation of a demo in a Pluralsight.com video called Design Patterns Library that was presented by David Starr
public class CustomerMapFactory
{
private Type[] _mapTypes;
public CustomerMapFactory()
{
LoadAvailableMaps();
}
//Return a newly created Type
public IXmlMapper CreateInstance(string customerId)
{
var t = GetTypeToCreate(customerId);
if (t == null) throw new Exception("Customer map not found");
return Activator.CreateInstance(t) as IXmlMapper;
}
//Find the map to instantiate
Type GetTypeToCreate(string customerId)
{
return _mapTypes.FirstOrDefault(tpMap => tpMap.Name.Contains(customerId));
}
//Identify all Types that use the IXmlMapper
private void LoadAvailableMaps()
{
_mapTypes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetInterface(typeof(IXmlMapper).ToString()) != null)
.ToArray();
}
}
}
Here is the program that utilizes the factory
class Program
{
private static void Main(string[] args)
{
//Same as above
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//Now utilizing the factory.
var mf = new CustomerMapFactory();
var poMap = mf.CreateInstance("BkMap");
var customerOrder = poMap.MapToCustomerOrder(po);
}
I've just started playing with the REST starter kit, and I've hit a road block trying to build my own service. I'm trying to create a service for account management, and I can't get the service to serialize my objects, throwing the following error:
Unable to deserialize XML body with root name 'CreateAccount' and root namespace '' (for operation 'CreateAccount' and contract ('Service', 'http://tempuri.org/')) using DataContractSerializer. Ensure that the type corresponding to the XML is added to the known types collection of the service.
Here's the actual code for the service (based off of the 'DoWork' method that came with the project):
[WebHelp(Comment = "Creates a Membership account")]
[WebInvoke(UriTemplate = "CreateAccount", RequestFormat = WebMessageFormat.Xml)]
[OperationContract]
public ServiceResponse CreateAccount(CreateAccount request)
{
try
{
// do stuff
return new ServiceResponse()
{
Status = "SUCCESS",
ErrorMessage = ""
};
}
catch (Exception ex)
{
return new ServiceResponse()
{
Status = "ERROR",
ErrorMessage = ex.Message + "\n\n" + ex.StackTrace
};
}
}
And last, but not least, here's the object that's causing all the trouble:
public class CreateAccount
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool SignUpForNewsletter { get; set; }
public string Password { get; set; }
}
Am I missing anything stupid?
Thanks in advance!
It turns out I was missing an extra value in the [DataContract] attribute on the business object.
Should be [DataContract(Namespace = "")]
It appears the problem is a namespace clash between your method name "CreateAccount" and your input type "CreateAccount".
Also, you have to mark your CreateAccount type as a DataContract like so:
[DataContract]
public CreateAccount
{
[DataMember]
public string LastName { get; set; }
...
}
If you want to keep the same name, you can specify a namespace for the CreateAccount class.
I noticed you have a return type as well. Ensure the return type is marked with the DataContract attribute as well. Also, specify the return format like so:
ResponseFormat = WebMessageFormat.Xml
If you don't have it already, I think a [DataContract] attribute above your CreatAccount class.
I had a similar problem, but I did have the DataContract attribute. What I was missing though was the xmlns="http://uri.org" attribute from the root element when trying to read the xml back into the object.
e.g.
<Root_Element xmlns="http://uri.org"><Child_Element/>...</Root_Element>