OData DataServiceContext query without DataServiceKey attribute - c#

On the server side I have an Entity Framework DbContext NorthwindContext and an System.Data.DataService<NorthwindContext>
My client needs to query the dataservice. I am able to retrive data with
var uriString = "http://localhost:8888/northwind.svc";
var context = new DataServiceContext(new Uri(uriString));
var query = context.CreateQuery<orders>("orders");
var result = query.ToList();
Ok so far this works. I don't use the visual studio tool chain for client proxy / class generation but create my own "copy" of the orders class which looks like this
[DataServiceKey("OrderID")]
public partial class orders
{
public int OrderID { get; set; }
public string CustomerID { get; set; }
...
}
This only works because I specified the DataServiceKey attribute. If I don't do that, I get an DataServiceQueryException
DataServiceKey is from Microsoft.Data.Service.Client which is referenced by my main assembly my model orders is in a seperate assembly where I don't want to have a reference to Microsoft.Data.Service.Client
Is it possible to use my query without needing to decorate my class with DataServiceKey attribute?

You cannot remove DataServiceKey from the entity you declare.
•Entity keys - each data class that is an entity type must have a key property. This property is attributed with the DataServiceKeyAttribute attribute ([DataServiceKeyAttribute]).
Link:
http://msdn.microsoft.com/en-us/library/dd723653(v=vs.113).aspx
Regards
Kajal

There are alternate data annotations when creating your own data model for OData.
See the System.ComponentModel.DataAnnotations and (depending on the framework version) System.ComponentModel.DataAnnotations.Schema. However, I've used these when creating my WCF DataService using DbContext, not DataServiceContext for the underlying model.

Related

How can I use Automapper to update an object in Entity Framework without nulling out properties?

I am trying to update an entity using Entity Framework and save it to the database. When the update is called, my service method retrieves the DTO, assigns its the values of the entity object that the UI passed to it, and then saves it to the database. Instead of manually assigning those values, i'd like to use Automapper, however when I do this, the values that I am not mapping are updated to null. Is there a way in Entity Framework or Automapper to prevent this?
Service method finds the existing object in the database, assigns the new entity's properties to it, then saves:
public void Update(MyEntity updatedEntity, int id)
{
var existingObject = db.tblmyentity.Find(id);
existingObject.name = updatedEntity.name;
existingObject.address = updatedEntity.address;
existingObject.phone = updatedEntity.phone;
db.SaveChanges();
}
However, there are values stored in fields of this object not accessible by the UI, such as who modified the object and when. Using AutoMapper to simplify this code (shown below) causes these fields to update to null:
public void Update(MyEntity updatedEntity, int id)
{
var existingObject = db.tblmyentity.Find(id);
Mapper.Map(updatedEntity, existingObject);
db.SaveChanges();
}
A good practice is to create a (service, api) model that contains only the relevant properties that can be updated. E.g.:
public class MyEntityServiceModel
{
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
}
// this looks differently in recent versions of AutoMapper, but you get the idea
Mapper.CreateMap<MyEntityServiceModel, MyEntity>();
// your update functions looks the same, except that it receives a service model, not a data model
Update(MyEntityServiceModel updatedEntity, int id)
{
// same code here
}
This approach has the following advantages:
you obtain what you are asking for
safety: you do not risk updating more properties than you should since the service model clearly specify the properties that should be updated
serialization: the service model is more appropriate if you need serialization (EF models may include unwanted navigation properties)
Update function consumer becomes unaware of the data persistence library you are using.

How to create a EntitySet or Model without creating corresponding table in database - Entity Framework

I have a stored procedure in my sqlserver database which is returning multiple resultset.
I am using following link from msdn to read multiple resultset from SP in entity framework.
https://msdn.microsoft.com/en-us/library/jj691402(v=vs.113).aspx
To read data, I need to have DBSets in xyzDBContext class for each of the resultsets.
Model Class:
public class AreaView
{
public String Area { get; set; }
public String Weight { get; set; }
}
DBContext:
public class EnsembleAPIContext : DbContext
{
public DbSet<AreaView> area {get; set;}
// I want to prevent this table from getting created in db
}
This is how I am reading resultset and mapping it with above created dbset.
reader.NextResult();
var contributionArea = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<ContributionArea>(reader, "area ", MergeOption.AppendOnly);
What I need to do is I want to create entity for these resultsets, but I dont want framework to create tables for these entities in database.
Note: Reason for doing this is, resultset which is returned by sp doesnt have a primary key, so suppose we can have a valid table created using entity without PK.
Is this possible?
Any help is appreciated.
The answer to "If we can skip creating tables in database with Entity framework is": Yes
Use [NotMapped] attribute.
[NotMapped]
public class Employee
{
public int ID { get; set; }
public String name { get; set; }
}
You can use this model for general purpose and table wont be created for this in database.
Another way of doing this is
In OnModelCreating() method
modelBuilder.Ignore<Employee>();
This way DBContext will ignore creating table for this model.
Can we create a DBSet<> without creating corresponding table in database
No. DbSet<T> represents a real database table or view.
To read data, I need to have DBSets in xyzDBContext class for each of the resultsets.
You don't. The ObjectContext.Translate method can be used to map DbReader to any class. The linked example is using entity types, but there is another Translate method overload which works for any type as described in Directly Executing Store Commands MSDN topic - Materializing the Result Type section.
With that being said, remove the DbSet from your context and use something like this:
var areaView = ((IObjectContextAdapter)db).ObjectContext.Translate<AreaView>(reader);

Use AutoMapper to map two VM to one Entity object

I'm using AutoMapper to map a lot of Entity models to View Model that I use in my controllers and views (.Net MVC)
There is a lot of relations in the DB and so our VM have a lot of childs (who have childs, and so and so)
public class InvoiceVMFull : VMBase
{
public int Id { get; set; }
public InvoiceType InvoiceType { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list all entity fields
public List<string> InvoiceMainAddress { get; set; }
public List<string> InvoiceDlvAddress { get; set; }
}
It works just fine, but is very slow and always load from the DB all relations whereas I usually need only a few datas...
So I created some light VM that I want to use for the majority of our pages.
public class InvoiceVMLite : VMBase
{
public int Id { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list only some of the entity fields (most used)
public StoredFileVM InvoiceFile { get; set; }
}
The problem is I can't find how :
to map one Entity object to the two VMs and how to choose the right one (to load from DB) using the context (the page or event called)
to map two VMs to one entity and save (on the DB) only the fields that are present in the VM used and don't erase the absent ones
I tried to create the mapping both VM :
Mapper.CreateMap<Invoice, InvoiceVMLite>();
Mapper.CreateMap<Invoice, InvoiceVMFull>();
But when I try to call the mapping for Lite, it doesn't exist (have been overridden by Full) :
Mapper.Map(invoice, InvoiceEntity, InvoiceVMLite)
Correct Use of Map function
It looks like you are calling map incorrectly. Try these instead
var vmLite = Mapper.Map<Invoice, InvoiceVMLite>(invoice);
var vmFull = Mapper.Map<Invoice, InvoiceVMFull>(invoice);
var vmLite = Mapper.Map(invoice); // would work if it were not ambiguous what the destination was based on the input.
Entity to two view models
You would usually create two mappings, one for each view model from the one entity. I'd suggest the cleanest is to have two separate views (separate Actions in a controller) for each view model. This may involve a quick redirect after you've decided on context which one to use.
View models to entity
Automapper is not meant for mapping from view models to Entities for many reasons, including the challenge you'd face. Instead you would pass specific parameters. The author of Automapper, Jimmy Bogard, wrote a good article on why this is the case.
I couldnt manage to do that with AutoMapper, and so I created my own convert methods (Entity <=> VM) with a lot of reflexivity, and with specific cases handled in each of the VM classes.
Now I can easily get a full or lite VM from an Entity, and also specify the depth in relation I want to go. So it's A LOT faster and more adaptable than AutoMapper
And I can save a VM to an entity (only saving modified fields if I want) that I create or that i got from base. So it's A LOT faster and adaptable than AutoMapper
In conclusion : Don't use autoMapper, it seem easy but create so many performance issues that it isn't worth it

Editing an NHibernate record via AJAX

I'm having trouble figuring out how to edit an existing entity when posting it to my controller. When I save a new Person, it works just fine, because Id isn't set, so NHibernate views it as a new entity and attaches it to the session. However, when I try to edit an existing entity, the MVC model binder can't set the Id, even though the JSON being posted has it properly set. So even though it's an existing entity, NHibernate again sees it as a new one, and then throws an exception because I'm calling .Update() on an entity that's not in the database or session.
Here's the code I'm using (obviously Person has a lot more properties, I just left them off to keep the code short)
Person class:
public class Person
{
public virtual int Id {get; private set;}
//... other properties
}
The JSON being posted to my edit action
{"Id": 10}
And in the controller
public JsonResult EditPerson(Person person)
{
Session.Update(person);
return Json(new { success = true});
}
I was always under the impression that you had to load the entity to get it into the session so that you could edit it.
so you would need code like
var entity = Session.Get<Entity>(entity.Id);
//make your changes
Session.Save(entity);
Try
public virtual int Id {get; protected set;}
NHibernate uses proxies to load and set the properties of your classes, if your setter is private (rather than public or protected) the proxy (which inherits from your mapped class) cannot access it and assign the value it loaded from the database.

Inheriting from EFF db model class leads to derived class properties being written into database

Let's say I have a EFF db model like this:
public class DbEFF
{
[Key]
public long Id { get; set; }
}
Now I'm creating a class where I'm inheriting from the db class like this:
public class DbTest:DbEFF
{
public DbTest(long id)
{
Id=id;
}
public string someotherproperty1 {get;set;}
}
Now I call the following code to write into the database:
var db = new DbEFF();
db.Id = "454545";
var model = new MasterEntities();
model.Table1.Add(db);
model.SaveChanges();
The weird thing now is that I get an inner exception saying that the column someotherproperty1 does not exist. What am I missing here? Why is the properties of the derived class being exposed like this?
The exception I'm getting is:
{"Invalid column name 'someotherproperty1'."}
In entity framework code first table per Hierarchy (TPH) is the default mapping.
This means that EF will map both DbTest and DbEFF to the same table. It will also add a column called Discriminator to see store what type of object is persisted (DbTest or DbEFF) in a particular row.
From the error you get it seems that your database already exist and that it has been created before you added the 'someotherproperty'. I.e. your table doesn't have 'someotherproperty' column.
To fix this there are several options, you need to get the schema of the table to match your classes or you must choose a different mapping strategy.
The easiest fix is to simply drop your database and let EF create the right database for you.

Categories