I am using Entity Framework to query a db which is defined by a model: inside this model I have several classes having a #region dynamic values:
[DataContract]
public class Job : AbstractEntity, IJob
{
[DataMember]
public virtual Guid Id { get; set; }
...
#region dynamic values
[DataMember]
public virtual string MetadataValue { get; set; }
[DataMember]
public virtual string ParametersValue { get; set; }
[DataMember]
public virtual string AttributesValue { get; set; }
#endregion
#region links
...
#endregion
}
AttributesValue, MetadataValue and ParametersValue are declared as string but are stored inside the db as XML documents. I am aware that this is not consistent with the model and should be changed, but for some reasons it has been managed this way and I am not allowed to modify it.
I have created a Unit Test in order to better handle the problem, and here is the code:
public class UnitTest1
{
private ModelContext mc;
[TestInitialize]
public void TestInit()
{
IModelContextFactory mfactory = ModelContextFactory.GetFactory();
mc = mfactory.CreateContextWithoutClientId();
}
[TestMethod]
public void TestMethod1()
{
DbSet<Job> jobs = mc.Job;
IQueryable<string> query = jobs
.Where(elem => elem.AttributesValue == "<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>")
.Select(elem => elem.AttributesValue);
List<string> attrs = new List<string>(query);
foreach (string av in attrs)
{
Console.WriteLine(av ?? "null");
}
Assert.AreEqual(1, 1);
}
}
A quick explanation about the TestInit and ModelContext:
ModelContext inherit from DbContext and is an abstract class implemented by SqlModelContext and OracleModelContext (both override OnModelCreating). Depending on the connection string, CreateContextWithoutClientId return a SqlModelContext or an OracleModelContext. Summary: a Factory pattern.
Let's get down to brass tacks: the TestMethod1.
The problem here is in the Where method and the error returned is, as expected:
SqlException: The data types nvarchar and xml are incompatible in the equal to operator.
(From now on I will only consider the AttributesValue property)
I thought of some possible solutions, which are:
Creating a new property inside the model (but not mapped to the db) and use it as a "proxy" instead of accessing directly AttributesValue. However only mapped properties can be used in Linq, so I discarded it.
Operating directly on the inner SQL query generated by the IQueryable and using a customized CAST for Oracle and Sql Server db. I'd rather avoid go for this for obvious reasons.
Is there a way to specify a custom Property Getter so that I can cast AttributesValue to string before it is accessed? Or maybe some configuration on the DbModelBuilder?
I'm using standard Entity Framework 6, Code-First approach.
There is no standard xml data type or standard canonical function for converting string to xml or vice versa.
Fortunately EF6 supports the so called Entity SQL Language which supports an useful construct called CAST:
CAST (expression AS data_type)
The cast expression has similar semantics to the Transact-SQL CONVERT expression. The cast expression is used to convert a value of one type into a value of another type.
It can be utilized with the help of the EntityFramework.Functions package and Model defined functions.
Model defined functions allow you to associate Entity SQL expression with user defined function. The requirement is that the function argument must be an entity.
The good thing about Entity SQL operators is that they are database independent (similar to canonical functions), so the final SQL is still generated by the database provider, hence you don't need to write separate implementations for SqlServer and Oracle.
Install the EntityFramework.Functions package through Nuget and add the following class (note: all the code requires using EntityFramework.Functions;):
public static class JobFunctions
{
const string Namespace = "EFTest";
[ModelDefinedFunction(nameof(MetadataValueXml), Namespace, "'' + CAST(Job.MetadataValue AS String)")]
public static string MetadataValueXml(this Job job) => job.MetadataValue;
[ModelDefinedFunction(nameof(ParametersValueXml), Namespace, "'' + CAST(Job.ParametersValue AS String)")]
public static string ParametersValueXml(this Job job) => job.ParametersValue;
[ModelDefinedFunction(nameof(AttributesValueXml), Namespace, "'' + CAST(Job.AttributesValue AS String)")]
public static string AttributesValueXml(this Job job) => job.AttributesValue;
}
Basically we add simple extension method for each xml property. The body of the methods doesn't do something useful - the whole purpose of these methods is not to be called directly, but to be translated to SQL when used inside LINQ to Entities query. The required mapping is provided through ModelDefinedFunctionAttribute and applied via package implemented custom FunctionConvention. The Namespace constant must be equal to typeof(Job).Namespace. Unfortunately due to the requirement that attributes can use only constants, we can't avoid that hardcoded string as well as the entity class / property names inside the Entity SQL string.
One thing that needs more explanation is the usage of '' + CAST. I wish we could use simply CAST, but my tests show that SqlServer is "too smart" (or buggy?) and removes the CAST from expression when used inside WHERE. The trick with appending the empty string prevents that behavior.
Then you need to add these functions to entity model by adding the following line to your db context OnModelCreating override:
modelBuilder.AddFunctions(typeof(JobFunctions));
Now you can use them inside your LINQ to Entities query:
IQueryable<string> query = jobs
.Where(elem => elem.AttributesValueXml() == "<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>")
.Select(elem => elem.AttributesValue);
which translates to something like this in SqlServer:
SELECT
[Extent1].[AttributesValue] AS [AttributesValue]
FROM [dbo].[Jobs] AS [Extent1]
WHERE N'<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>'
= ('' + CAST( [Extent1].[AttributesValue] AS nvarchar(max)))
and in Oracle:
SELECT
"Extent1"."AttributesValue" AS "AttributesValue"
FROM "ORATST"."Jobs" "Extent1"
WHERE ('<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>'
= ((('')||(TO_NCLOB("Extent1"."AttributesValue")))))
Related
Our project has a framework and an own code base, which implements entities of the framework.
The idea is also to have several indexes in the framework that will return the results of all inheritances of certain framework types like user.
Apparantly, the only way Raven supports this (without creating the index on the highest level and manually adding maps) is to store all objects in the same collection by overwriting the Clr-type. However, this means we lose the derived information and cannot query on it.
Some samples:
class A {
public string Id {get; set; },
public string Name {get; set; }
}
class B : A { }
class C : A { }
class D : C { }
Then I want to query something along the lines of:
store.Query<IndexOfA>().Where(a => a.Name == "foo").As<A>().ToList();
AND be able to still do this;
store.Query<IndexOfC>().As<C>().ToList()
My idea was to add a convention that saves both the derived and the base class to the metadata when storing documents in RavenDB, but I have no idea how to go about this and cannot find any documentation on the subject.
Any ideas?
You can create an index that used a multi map index with AddMapForAll<Base>, which will generate a separate entry for each derived class.
You could then use that index to do polymorphic queries.
Using MongoDB as my data store makes me to have ObjectID type as primary key by Default. It also can be changed by using Guid with [BsonId] attribute. Which is also defined in MongoDB C# Driver library. I would like to have my Entities independent from Data layer.
Can I just use name Id for the property to identify primary key? What else I can try?
You can use BsonClassMap instead of using attributes to keep your classes "clean".
// 'clean' entity with no mongo attributes
public class MyClass
{
public Guid Id { get; set; }
}
// mappings in data layer
BsonClassMap.RegisterClassMap<MyClass>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id).SetIdGenerator(CombGuidGenerator.Instance);
});
OPTION 1: Stick with BsonId and use the Facade Pattern
The [BsonId] property is what you'd use to indicate that the _id property should be linked to a specific property. There isn't a way around that (short of ignoring _id entirely in your crud operations which seems like a bad idea).
So, if you want to separate your "entity" object from your "data layer" then just use a poco class.
-- Use a poco class as a substitute for a record. That class is only for data storage: a quick way to get data in/out of mongo, and a great alternative to working with bson documents.
-- Use a facade on top of that poco class for your entity layer. I don't find it useful to re-invent the wheel, so I typically ask our devs have the entity interface inherit the data-layer (poco) interface, but you can do it however you'd like
Breaking up a sample MyObject class
IMyObjectRecord (declared at the dal and contains only properties and mongo-specific attributes)
IMyObject:IMyObjectRecord (declared at the entity level and may include added properties and methods)
MyObjectRecord:IMyObjectRecord (declared inside the dal, contains mongo-specific attributes. Could be declared internal if you wanted to be really strict about separation).
MyObject:IMyObject (could be, for example, a facade on top of the IMyObjectRecord class you pull from the dal).
Now - you get all the benefits of the facade, and you have a hard-coded link between the properties BUT, you get to keep Bson attributes contained in your dal.
OK, fine. But I really really really HATE that answer.
Yeah. I can accept that. OK, so how about a Convention Pack? If you ABSOLUTELY PROMISE that you'll call your Id's "Id" and you SWEAR that you'll type them as strings (or -- use some other convention that is easy to identify), then we could just use a convention pack like the one I stole from here
namespace ConsoleApp {
class Program {
private class Foo {
// Look Ma! No attributes!
public string Id { get; set; }
public string OtherProperty { get; set; }
}
static void Main(string[] args) {
//you would typically do this in the singleton routine you use
//to create your dbClient, so you only do it the one time.
var pack = new ConventionPack();
pack.Add(new StringObjectIdConvention());
ConventionRegistry.Register("MyConventions", pack, _ => true);
// Note that we registered that before creating our client...
var client = new MongoClient();
//now, use that client to create collections
var testDb = client.GetDatabase("test");
var fooCol = testDb.GetCollection<Foo>("foo");
fooCol.InsertOne(new Foo() { OtherProperty = "Testing", Id="TEST" });
var foundFoo = fooCol.Find(x => x.OtherProperty == "Testing").ToList()[0];
Console.WriteLine("foundFooId: " + foundFoo.Id);
}
//obviously, this belongs in that singleton namespace where
//you're getting your db client.
private class StringObjectIdConvention : ConventionBase, IPostProcessingConvention {
public void PostProcess(BsonClassMap classMap) {
var idMap = classMap.IdMemberMap;
if (idMap != null && idMap.MemberName == "Id" && idMap.MemberType == typeof(string)) {
idMap.SetIdGenerator(new StringObjectIdGenerator());
}
}
}
}
}
What's a Convention Pack
It's a little set of mongo "rules" that get applied during serialize/deserialize. You register it once (when you setup your engine). In this case, the sample pack is telling mongo "if you see a field called 'Id', then save it as a string to _id, please."
These can get really complex and fun. I'd dig into convention packs if you really really really hate the other approach. It's a good way to force all your mongo "attribute driven" logic into one self-contained location.
I have stumbled on the same problem myself, and I didn't want to have mongo attributes inside my classes.
I have created a small wrapper example to show how I save and find elements without having an Id property on the data classes of my business logic.
The wrapper class:
public static class Extensions
{
public static T Unwrap<T>(this MongoObject<T> t)
{
return t.Element;
}
}
public class MongoObject<T>
{
[BsonId]
private ObjectId _objectId;
public T Element { get; }
public MongoObject(T element)
{
Element = element;
_objectId = new ObjectId();
}
}
I have also added an extension method to easily unwrap.
Saving an element is simple
public void Save<T>(T t)
{
_collection.InsertOne(new MongoObject<T>(t));
}
To find an element we can do a linq-like query:
Say we have a data class:
public class Person
{
public string Name { get; set; }
}
then we can find such an element by
public Person FindPersonByName(string name)
{
return _collection.AsQueryable().FirstOrDefault(
personObject => personObject.Element.Name == name).Unwrap();
}
We can also generalize by making MongoObject implement IQueryable<T> and this would make the use of the wrapper even more convenient
If i understand correctly. You want to put your entity to other layer without attribute.
I think you can try this
public object Id { get; set; }
after that you can put your Id which is coming from mongodb without attribute
I have the following class:
public class BicycleSellerListing : IUserName
{
public UserProfile UserProfile { get; set; }
/// <summary>
/// IUserName interface property
/// </summary>
public string UserName
{
get
{
return UserProfile.UserName;
}
}
}
The interface:
public interface IUserName
{
string UserName { get; }
}
And this query:
public static List<T> GetList<T>(string userName) where T : class, IUserName
{
using (SqlUnitOfWork work = new SqlUnitOfWork())
{
return work.GetList<T>(row => row.UserName == userName)
.ToList();
}
}
When I execute this query, I get the following exception:
The specified type member 'UserName' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
I understand why I'm getting the exception, but am wondering if there is a way I can perform this query using the interface?
To answer you quistion:
Is it possible to query on an interface property?
Yes. it's no problem. The fault you are getting is not becuase of the interface.
The problem is that you can't query on properties that isn't mappe with Linq 2 Entities
Other people have had this propblem as well.
The underlying expressionbuilder can not distinct between properties that is mapped to the database, and properties that is not.
It is a problem becuase the compiler can't help you.
In Linq to object, it is no problem, so the compiler doesnt throw any errors/warnings
You should try to make it clear the this property is not mapped - perhaps by a prefix, or a nested class that contains all the "custom" properties.
In addition to the existing answer(s), you can perform the where in memory, but that means retrieving the whole table.
Normally I wouldn't recommend this though.
public static List<T> GetList<T>(string userName) where T : class, IUserName
{
using (SqlUnitOfWork work = new SqlUnitOfWork())
{
return work.GetList<T>()
.AsEnumerable()
.Where(row => row.UserName == userName)
.ToList();
}
}
Some workarounds:
You could try to determine underlying entity type in runtime and
then dynamically compile and invoke a generic method executing query
with this type.
It is possible to assemble this query manually in runtime using
Expression class
You could try Entity SQL query syntax instead of Linq
Inspired by a desire to be able to use enumerations in EF queries, I'm considering adding an ExpressionVisitor to my repositories that will take incoming criteria/specifications criteria and rewrite them to use the corresponding persisted int property.
I'm consistently using the following Value-suffix pattern in my (code-first) entities:
public class User : IEntity
{
public long ID { get; set; }
internal int MemberStatusValue { get; set; }
public MemberStatus MemberStatus
{
get { return (MemberStatus) MemberStatusValue; }
set { MemberStatusValue = (int) value; }
}
}
And map this to the database using the following:
internal class UserMapping : AbstractMappingProvider<User>
{
public override void DefineModel( DbModelBuilder modelBuilder )
{
// adds ToTable and other general mappings
base.DefineModel( modelBuilder );
Map.Property( e => e.MemberStatusValue ).HasColumnName( "MemberStatus" );
}
}
In my repositories I have the following method:
public IQueryable<T> Query( Expression<Func<T, bool>> filter, params string[] children )
{
if( children == null || children.Length == 0 )
{
return Objects.Where( filter );
}
DbQuery<T> query = children.Aggregate<string, DbQuery<T>>( Objects, ( current, child ) => current.Include( child ) );
return filter != null ? query.Where( filter ) : query;
}
I'd like to add a method call inside this method to rewrite the filter expression, replacing all references to the MemberStatus property with references to MemberStatusValue.
I suppose it will be a solution involving something like seen in this SO post, but I'm not sure exactly how to get from idea to implementation.
If you can give any advice on the potential performance impact of adding this feature, that would also be appreciated.
I'm not sure whether this is quite what you're after, but I've found it simpler to handle enums in a similar but slightly different way. To wit, I have two properties, as you do, but my int property is public and is what the database persists; I then have another public "wrapper" property that gets/sets the int property value via casts from/to the desired enumerated type, which is what's actually used by the rest of the application.
As a result, I don't have to mess around with the model; EF understands and persists the int property just fine while the rest of the application gets nice interactions with the enum type. The only thing I don't like about my approach is that I have to write my LINQ statements with a bunch of casts on any enum value I'm trying to query to turn it into an int to match against the field that's actually persisted. It's a small price, however, and I'd like to suggest it to you because it appears to me that you're using a string to generate your query which gives up all the type safety, Intellisense, etc. that LINQ provides.
Finally, if you're interested in a walkthrough of how to use the new enum features in EF 5 (which is available in beta for download now if you'd like to try it out), check this out:
http://msdn.microsoft.com/en-us/hh859576
Let's say I need to display a list of customers, but only want to display the Name and somehow associate the key to the name within a list control.
It would probably be costly to retrieve the entire list of customers and all it's properties. In this scenario, would it be better to create another class with the properties that are required (in this case Id and Name)?
A basic implementation could look like this:
public class Customer {
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
.....
}
public class CustomerListView {
public int Id { get; set; }
public string Name { get; set; }
}
public interface IRepository<T> {
public T Find(int id);
public IEnumerable<T> FindAll();
....
}
public class Repository<T>: IRepository<T> {
....
}
public class CustomerRepository: Repository<Customer> {
public IEnumerable<CustomerListView> FindAllListView();
}
Would this approach be appropriate? What other options would there be?
In order to achieve such goals, I create a simple 'View' class, for example CustomerView, which just contains the properties that are needed to display an overview.
My Repository then has a method which returns a collection of these CustomerView objects.
I mostly use NHibernate in my projects. Nhibernate allows you to use 'projections'.
So, what I do in my repository is this:
(note that the code below is just some pseudo-code; it won't compile).
public IList<CustomerView> GetAllCustomers()
{
ICriteria crit = _session.CreateCriteria (typeof(Customer));
crit.AddProjection ( ... );
crit.SetResultTransformer (new EntityToBeanTransformer(typeof(CustomerView));
return crit.ToList();
}
In fact, it comes down to this: I tell my O/R mapper that it should query Customers, but that it should return entities of type 'CustomerView'.
In the defintion of the projection, I also define which properties of the Customer class map to which properties of the CustomerView class.
Then, the O/R mapper is smart enough to generate a very simple query, which only retrieves those fields that are required to populate the CustomerView class.
For instance, the query that is executed can be as simple as:
SELECT customerid, customername FROM tblCustomer
If you use IQueryable as your return instead of IEnumerable than there is no cost of doing:
CustomerRepository().GetAll().Find(1) because AsQueryable doesn't actually execute until you request data. That means LINQ can optimize it out to a:
SELECT .... FROM .... WHERE ID = 1 instead of
GET EVERYTHING. FIND WHERE THE ID = 1
See this post for an explanation:
Why use AsQueryable() instead of List()?
Using this approach you could create an anonymous class and futher narrow down the data going over the wire to just what you want. That way the query generated by LINQ is optimized to the fullest.
If you have to retrieve the list form a Database then your proposal makes some sense but I would look into a Linq and anonymous type solution.
If the list of Customers already exists in memory then there there are no savings.
You could combine the techniques used by Nissan and Frederik (anonymous types and NHibernate) by using Linq-to-NHibernate.
Item #31 in Bill Wagner's More Effective C# says "limit type scope by using anonymous types", and I agree. BTW, I recommend the whole book.