Primary Key Error on .Find with single Id Entity Framework - c#

I am having an issue with performing a .find on an entity with a single PK.
The base class is as follows;
public abstract class Entity : IEntity
{
public int Id { get; set; }
public bool? IsArchived { get; set; }
}
and the class is
public class Manufacturer : Entity
{
public string Name { get; set; }
}
however, when i attempt a HTTPPUT on the Manufacturer i get the following error;
The number of primary key values passed must match number of primary key values defined on the entity.
Parameter name: keyValues
This is thrown when I call the .find method on the set;
public T Find<T>(int id, params Expression<Func<T, object>>[] includes) where T : class, IEntity
{
return this.Set<T>().Find(id, includes);
}
Any ideas why this is occurring?
Update
I am running Code first. The database has generated (this is through VS table design) PK_dbo.Manufacturers (Primary Key, Clustered: Id)
I am operating a SOLID architecture, where my controller is as follows;
[HttpPut]
[Route("")]
public Manufacturer Put(Manufacturer m)
{
var response = ManufactureService.UpdateManufacturer(m);
return response.Result;
}
where the ManufacturerService is as follows;
public ServiceResponse<Manufacturer> UpdateManufacturer(Manufacturer obj)
{
Func<Manufacturer> func = delegate
{
using (var context = _contextFactory())
{
var manufacturer = context.Find<Manufacturer>(obj.Id);
manufacturer.Name = obj.Name;
manufacturer.IsArchived = manufacturer.IsArchived;
context.Update(manufacturer);
return manufacturer;
}
};
return this.Execute(func);
}
where obj is the passed Manufacturer to be updated. and the id is an integer.
to give a little more info, I have a contect which then uses the following;
public T Find<T>(int id, params Expression<Func<T, object>>[] includes) where T : class, IEntity
{
return this.Set<T>().Find(id, includes);
}

You pass two key values to Find but EF would only deduce that Id is a PK. Do you really want a nullable bool as part of your PK? I'm not sure if this is allowed. If you do want the nullable bool as part of your key then I think you will need to let EF know about it.

If its failing on Manufacturer, and the only column is Name, then it makes zero sense that your call stack is blaming Find(int id). There is no integer primary key on this table. Maybe that is your problem.
UPDATE
Please try copying the code in the Entity class into Manufacturer. Then remove the subclass relationship. Then see if your put still fails.

Related

Mapping properties onto an IQueryable

We have an API with IQueryable<T> resources because we work with OData. My boss requires the resources to be exposed to also have additional information which is not available in the database.
OData allows one to only select the information they need, it does this by mapping a $select=Quantity query parameter to LINQ: performing a .Select(s => s.Quantity). This will of course not work since LINQ to Entities will complain the column does not exist in the database.
public class Item
{
public int Id { get; set; }
public int Quantity
{
get { return SubItems.Count; }
set {}
}
public virtual ICollection<SubItem> SubItems { get; set; }
}
I need a way around this restriction. I have looked into AutoMapper however it fails when using .ProjectTo() with their IValueResolver. I need IValueResolver because some of the extra properties are not so easily mapped onto a SQL structure, for example:
public class Item
{
public int Id { get; set; }
public string Description
{
get { return Convert(DescriptionId); }
set {}
}
public int DescriptionId { get; set; }
}
public string Convert(int value)
{
switch(value)
{
case 1:
return "1";
default:
return "0";
}
}
I have looked into the code below as well but this fails with System.ArgumentException: 'Cannot apply ODataQueryOptions of 'DtoItem' to IQueryable of 'Item'. Parameter name: query'. Reversing the data types I manage to get a result but I cannot return this of course as the data type is not matching with my controller function. I do however need the ODataQueryOptions of DtoItem otherwise I will not have access to $select any of those extra fields because it will not know about them. But then I am back to square one on how would OData even apply those options to an item of a different type.
public IQueryable<Item> Get(ODataQueryOptions queryOptions)
{
IQueryable<Item> items = (IQueryable<Item>) queryOptions.ApplyTo(repositoryItem.Select());
return mapper.Map<List<DtoItem>>(items.ToList()).AsQueryable(); // Cannot convert DtoItem to Item but of course I want to return DtoItem
}
What is necessary for me to accomplish this requirement? I have looked into Entity Framework but it seems very much sealed of. I'm not even sure if modifying the expression tree would be enough. I'd still have to add the values somehow during materialization.

How to get id from Add using UnitOfWork pattern?

I am using the UnitOfWork pattern to abstract database access in my Asp.Net application. Basically I follow the UnitOfWork pattern approach described here:
https://chsakell.com/2015/02/15/asp-net-mvc-solution-architecture-best-practices/
However, I'm struggling to understand, how I will get the Id of a newly added item. Like if I want to add a new customer to my Customer repository, how will I get the customer id? The problem is that Add and Commit are decoupled, and the Id is not known until after Commit.
Is there a way to get the id of an added item, using the UnitOfWork pattern?
My approach is as follows. Simply continue working with the added entity as an object. So instead of returning it's ID, return the added object itself. Then, at some point (typically in the controller) you will call UoW.Commit(); and as a result, the Id property of the added entity will contain the updated value.
At this point, you can start using the Id property and for example store it in a cookie as you said.
Note that I dont want my EF model classes to propagate to my domain layer
I have done a workaround. I think it works pretty well
When you want a repository, for example of DbCars, and you insert a new DomainCar you want to get that Id that was only generated when SaveChanges() is applied.
public DomainCar //domain class used in my business layers
{
public int Id{get;set;}
public string Name{get;set;}
}
public DbCar //Car class to be used in the persistence layer
{
public int Id{get;set;}
public string Name{get;set;}
public DateTime CreatedDate{get;set;}
public string CreatedBy{get;set;}
}
First you create a generic IEntity interface and a class implementing it:
public interface IEntity<T>
{
T Id { get; }
}
public class Entity<T> : IEntity<T>
{
dynamic Item { get; }
string PropertyName { get; }
public Entity(dynamic element,string propertyName)
{
Item = element;
PropertyName = propertyName;
}
public T Id
{
get
{
return (T)Item.GetType().GetProperty(PropertyName).GetValue(Item, null);
}
}
}
Then in your add method of the repository you return a IEntity of the type of your Id:
public IEntity<int> AddCar(DomainCar car)
{
var carDb=Mapper.Map<DbCar>(car);//automapper from DomainCar to Car (EF model class)
var insertedItem=context.CARS.Add(carDb);
return new Entity<int>(insertedItem,nameof(carDb.Id));
}
Then , somewhere you are calling the add method and the consequent Save() in the UnitofWork:
using (var unit = UnitOfWorkFactory.Create())
{
IEntity<int> item =unit.CarsRepository.AddCar(new DomainCar ("Ferrari"));
unit.Save(); //this will call internally to context.SaveChanges()
int newId= item.Id; //you can extract the recently generated Id
}
The problem here is that the id is generated by the database, so we need to call SaveChanges so that the database generates the id and EntityFramework will fix the entity up with the generated id.
So what if we could avoid the database roundtrip?
One way to do this is to use a uuid instead of an integer as an id. This way you could simply generate a new uuid in the constructor of your domain model and you could (pretty) safely assume that it would be unique across the entire database.
Of course choosing between a uuid and an integer for the id is an entire discussion of its own: Advantages and disadvantages of GUID / UUID database keys But at least this is one point in favor of a uuid.
Unit of work should be a transaction for the entire request.
Simply just return the person id from the newly created object.
Depending on what technology you are using for your data access this will differ, but if you are using Entity Framework, you can do the following:
var person = DbContext.Set<Person>().Create();
// Do your property assignment here
DbContext.Set<Person>().Add(person);
return person.Id;
By creating the Person instance this way, you get a tracked instance that allows for lazy loading, and using the Id property, as it will be updated when SaveChanges is called (by ending your unit of work).
Instead of IDENTITY, I use SEQUENCE at database level. When a new entity is being created, first, I get the next value of the sequence and use it as Id.
Database:
CREATE SEQUENCE dbo.TestSequence
START WITH 1
INCREMENT BY 1
CREATE TABLE [dbo].[Test](
[Id] [int] NOT NULL DEFAULT (NEXT VALUE FOR dbo.TestSequence),
[Name] [nvarchar](200) NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([Id] ASC)
)
C#:
public enum SequenceName
{
TestSequence
}
public interface IUnitOfWork : IDisposable
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;
void Commit(SqlContextInfo contextInfo);
int NextSequenceValue(SequenceName sequenceName);
}
public class UnitOfWork : MyDbContext, IUnitOfWork
{
...
public void Commit(SqlContextInfo contextInfo)
{
using (var scope = Database.BeginTransaction())
{
SaveChanges();
scope.Commit();
}
}
public int NextSequenceValue(SequenceName sequenceName)
{
var result = new SqlParameter("#result", System.Data.SqlDbType.Int)
{
Direction = System.Data.ParameterDirection.Output
};
Database.ExecuteSqlCommand($"SELECT #result = (NEXT VALUE FOR [{sequenceName.ToString()}]);", result);
return (int)result.Value;
}
...
}
internal class TestRepository
{
protected readonly IUnitOfWork UnitOfWork;
private readonly DbSet<Test> _tests;
public TestRepository(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
_tests = UnitOfWork.Set<Test>();
}
public int CreateTestEntity(NewTest test)
{
var newTest = new Test
{
Id = UnitOfWork.NextSequenceValue(SequenceName.TestSequence),
Name = test.Name
};
_tests.Add(newTest);
return newTest.Id;
}
}
I don't think there is a way to do that unless you break the pattern and pass in some extra information about the newly created entity.
Since the Id will only be allocated since commit is successful and if you don't have information about which entities were created/updated/deleted, its almost impossible to know.
I once did it using the code below (I don't recommend it though but I use it for this need specifically)
public string Insert(Person entity)
{
uow.Repository.Add(entity); //public Repository object in unit of work which might be wrong
Response responseObject = uow.Save();
string id = entity.Id; //gives the newly generated Id
return id;
}
My solution is returning Lazy<MyModel> by repository method:
public class MyRepository
{
// ----
public Lazy<MyModel> Insert(MyModel model)
{
MyEntity entity = _mapper.MapModel(model);
_dbContext.Insert(entity);
return Lazy<MyModel>(()=>_mapper.MapEntity(entity));
}
}
And in the domain:
Lazy<MyModel> mayModel = unitOfWork.MyRepository.Insert(model);
unitOfWork.Save();
MyModel myModel = myModel.Value;
To expand on Martin Fletcher his answer:
EF Core generates (depending on the db provider) a temporary value for the generated id, once you add the entity to the DbContext, and start tracking it. So before the unit of work is actually committed via SaveChanges(). After SaveChanges() is called, the DbContext will actually fix up all placeholder ids with the actual generated id.
A nice example (which I quote from this answer):
var x = new MyEntity(); // x.Id = 0
dbContext.Add(x); // x.Id = -2147482624 <-- EF Core generated id
var y = new MyOtherEntity(); // y.Id = 0
dbContext.Add(y); // y.Id = -2147482623 <-- EF Core generated id
y.MyEntityId = x.Id; // y.MyEntityId = -2147482624
dbContext.SaveChangesAsync();
Debug.WriteLine(x.Id); // 1261 <- EF Core replaced temp id with "real" id
Debug.WriteLine(y.MyEntityId); // 1261 <- reference also adjusted by EF Core
More information:
https://learn.microsoft.com/en-us/ef/core/change-tracking/explicit-tracking#generated-key-values
https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#primary-keys
Another option is to generate the keys on the client side (this is possible with for example UUID-based primary keys). But this has been discussed in other answers.

Get Id of Generic Entity of EF 6

I write a generic repository to CRUD in EF.In add method is written like this
public class GenericRepo<T> where T : class
{
//Create
public static void Add(CellPhoneProjectEntities dbContext, T entity)
{
dbContext.Set<T>().Add(entity);
dbContext.SaveChanges();
}
}
works fine by i want to get first element of the Entity(Primary key ,Identity column )
public static long Add(CellPhoneProjectEntities dbContext, T entity,long id)
{
dbContext.Set<T>().Add(entity);
dbContext.SaveChanges();
var pk = entity.ElementType.KeyMembers[0];
//Something like first elemnt by generic
return pk as Long;
}
can anyone help to get First element(Id) of entity after insertion?
EDIT: EF database first and my primary key is not named Id..
Rather it is TableNameId eg. Table ProjectMaster has primary key ProjectMasterId
When you assume that the element's ID is a long that's the first element of they key, you're making explicitly non-generic assumptions. Not all entities have a long key, or a key at all.
If you have this assumption, it's best to have it explicitly stated in the code. Create a base entity class with an ID, and ensure all entities inherit it. That way you know each entity has a compatible ID. If your table's column is named differently, you can use the [Column] attribute to map between them:
public abstract class EntityBase
{
public virtual long Id {get; set;}
}
public class MyEntity : EntityBase
{
[Column("TableId"]
public override long Id {get;set;}
}
public long Add(DbContext context, T entity) where T : EntityBase
{
var storedEntity = dbContext.Set<T>().Add(entity);
dbContext.SaveChanges();
return storedEntity.Id; // Will always have the property.
}
After trying I find a temporary solution..but better answer is still not found ...
1 Mistake I made that not every entity (Table) contains id ..it is rather TableId
so my temporary solution is return the entity and get the value of identity column after added to db.
//Create ///Get ID of the table
public static T Add(CellPhoneProjectEntities dbContext, T entity, long id)
{
dbContext.Set<T>().Add(entity);
dbContext.SaveChanges();
return entity;
}
and to Get id
ServiceMaster master=some entity;;
long id=GenericRepo<ServiceMaster>Add(dbContext,master).ServiceMasterId;

Entity framework: writing a common method to check exist

My domain class:
public class Address
{
[Key]
public virtual string AddressId { get; set; }
public virtual string Address { get; set; }
}
In my MVC controller I want to check the given Address exist, before I insert.
public ActionResult Create(Address address)
{
if (ModelState.IsValid)
{
if (db.Addresses.Any(a => a.AddressId == address.AddressId)) // how I do it now
{
ModelState.AddModelError(string.Empty, "Address Id already exists!");
}
else
{
db.Addresses.Add(address);
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
But there are lot of other domain classes in my project and I want to do the same check again and again.
My question is I want to write a generic method in my Db context class to perform this check. (looks like below or similar)
public bool Exists(object) {
// return true if exist
}
i.e. a method which I can call like this:
db.Exists(address)
Thanks!
You could use generics and do something like the following:
public class YourDbContext : DbContext
{
...
public bool Exists<TEntity>(object id)
where TEntity : class
{
var dbSet = Set<TEntity>();
var entity = dbSet.Find(id);
return entity != null;
}
Which you'd then use like:
db.Exists<Address>(address.AddressId);
Using Find isn't the most efficient way to handle this, but it has the key benefit that you're not required to know what the actual primary key property on the class is, which would greatly complicate this method. For example, Address has AddressId, but Foo might have FooId.
UPDATE
Since ultimately this just uses Find under the hood, you just have to modify the method slightly to be able to take multiple parameters. Find handles composite keys by allowing one more parameters to be passed to it. But bear in mind, the the order matters and must align with the key order you specified when configuring your entity.
public bool Exists<TEntity>(params object[] keys)
where TEntity : class
{
var dbSet = Set<TEntity>();
var entity = dbSet.Find(keys);
return entity != null;
}

Entity Framework CTP5 Code First: using non-primitive type as key

I have a project which has quite a few data-structures, and I want to introduce a database into it. I'm looking into Entity Framework CTP5's code-first feature.
Now, I'm using a distinct Identifier type for each of my types (e.g. TypeAIdentifier, TypeBIdentifier, etc.). It's nice because it allows for type safety when working with these identifiers. The problem is that I can't seem to get EF to use a non-primitive-type as an identifier. There's a hint in http://blogs.msdn.com/b/efdesign/archive/2010/03/30/data-annotations-in-the-entity-framework-and-code-first.aspx:
KeyAttribute
KeyAttribute is used to specify that a property/column is part of the primary
key of the entity and applies to scalar properties only.
Indeed, the following program throws an exception:
class Program
{
static void Main(string[] args)
{
MyDb db = new MyDb();
db.SaveChanges();
}
}
class MyDb : DbContext
{
public DbSet<MyObject> MyObjects { get; set; }
}
class MyObject
{
[Key] public MyIdentifierType MyId { get; set; }
public int IntValue { get; set; }
}
class MyIdentifierType
{
public int UniqueId { get; set; }
}
Strangely enough this program throws a MissingMethodException ("no parameterless constructor defined for this object"), but I'm sure this is related to the KeyAttribute because either of the following makes the program run:
Remove [Key]; or
Move [Key] to IntValue.
How can I get EF to map my Identifier classes as PKs? Two options that come to mind:
Figure out a way to get EF to use a non-primitive-type as a PK.
Put an 'int' as the PK and get EF to convert this 'int' to MyIdentifier (this has the disadvantage that if I ever decide to use something other than an 'int' I'll have to update both MyIdentifier and the mapping, but it will at least let me preserve the identifier type safety). I thought Function Imports would let me do this, but it seems that are built on top of stored procedures.
Is there a way to achieve this?
I'd convert the key with a property to the custom type.
public class MyObject
{
public int Id { get; set; }
public int IntValue { get; set; }
public MyIdentifierType MyId
{
get { return new MyIdentifierType {UniqueId = Id}; }
set { Id = value.UniqueId; }
}
}

Categories

Resources